Add support for device-tree-based drivers.
This commit is contained in:
parent
aa7585d04b
commit
1895c3806b
13 changed files with 519 additions and 52 deletions
|
@ -86,9 +86,11 @@ CPPFLAGS_TERMINAL_LIST += '-Dgrub_term_register_output(...)=OUTPUT_TERMINAL_LIST
|
||||||
CPPFLAGS_COMMAND_LIST = '-Dgrub_register_command(...)=COMMAND_LIST_MARKER(__VA_ARGS__)'
|
CPPFLAGS_COMMAND_LIST = '-Dgrub_register_command(...)=COMMAND_LIST_MARKER(__VA_ARGS__)'
|
||||||
CPPFLAGS_COMMAND_LIST += '-Dgrub_register_extcmd(...)=EXTCOMMAND_LIST_MARKER(__VA_ARGS__)'
|
CPPFLAGS_COMMAND_LIST += '-Dgrub_register_extcmd(...)=EXTCOMMAND_LIST_MARKER(__VA_ARGS__)'
|
||||||
CPPFLAGS_COMMAND_LIST += '-Dgrub_register_command_p1(...)=P1COMMAND_LIST_MARKER(__VA_ARGS__)'
|
CPPFLAGS_COMMAND_LIST += '-Dgrub_register_command_p1(...)=P1COMMAND_LIST_MARKER(__VA_ARGS__)'
|
||||||
|
CPPFLAGS_FDT_LIST := '-Dgrub_fdtbus_register(...)=FDT_DRIVER_LIST_MARKER(__VA_ARGS__)'
|
||||||
CPPFLAGS_MARKER = $(CPPFLAGS_FS_LIST) $(CPPFLAGS_VIDEO_LIST) \
|
CPPFLAGS_MARKER = $(CPPFLAGS_FS_LIST) $(CPPFLAGS_VIDEO_LIST) \
|
||||||
$(CPPFLAGS_PARTTOOL_LIST) $(CPPFLAGS_PARTMAP_LIST) \
|
$(CPPFLAGS_PARTTOOL_LIST) $(CPPFLAGS_PARTMAP_LIST) \
|
||||||
$(CPPFLAGS_TERMINAL_LIST) $(CPPFLAGS_COMMAND_LIST)
|
$(CPPFLAGS_TERMINAL_LIST) $(CPPFLAGS_COMMAND_LIST) \
|
||||||
|
$(CPPFLAGS_FDT_LIST)
|
||||||
|
|
||||||
# Define these variables to calm down automake
|
# Define these variables to calm down automake
|
||||||
|
|
||||||
|
|
|
@ -369,6 +369,16 @@ terminal.lst: $(MARKER_FILES)
|
||||||
platform_DATA += terminal.lst
|
platform_DATA += terminal.lst
|
||||||
CLEANFILES += terminal.lst
|
CLEANFILES += terminal.lst
|
||||||
|
|
||||||
|
fdt.lst: $(MARKER_FILES)
|
||||||
|
(for pp in $^; do \
|
||||||
|
b=`basename $$pp .marker`; \
|
||||||
|
sed -n \
|
||||||
|
-e "/FDT_DRIVER_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/i\1: $$b/;p;}" \
|
||||||
|
-e "/FDT_DRIVER_LIST_MARKER *( *\"/{s/.*( *\"\([^\"]*\)\".*/o\1: $$b/;p;}" $$pp; \
|
||||||
|
done) | sort -u > $@
|
||||||
|
platform_DATA += fdt.lst
|
||||||
|
CLEANFILES += fdt.lst
|
||||||
|
|
||||||
parttool.lst: $(MARKER_FILES)
|
parttool.lst: $(MARKER_FILES)
|
||||||
(for pp in $^; do \
|
(for pp in $^; do \
|
||||||
b=`basename $$pp .marker`; \
|
b=`basename $$pp .marker`; \
|
||||||
|
|
|
@ -159,6 +159,8 @@ kernel = {
|
||||||
arm_coreboot = kern/arm/coreboot/init.c;
|
arm_coreboot = kern/arm/coreboot/init.c;
|
||||||
arm_coreboot = kern/arm/coreboot/timer.c;
|
arm_coreboot = kern/arm/coreboot/timer.c;
|
||||||
arm_coreboot = kern/arm/coreboot/coreboot.S;
|
arm_coreboot = kern/arm/coreboot/coreboot.S;
|
||||||
|
arm_coreboot = lib/fdt.c;
|
||||||
|
arm_coreboot = bus/fdt.c;
|
||||||
|
|
||||||
terminfoinkernel = term/terminfo.c;
|
terminfoinkernel = term/terminfo.c;
|
||||||
terminfoinkernel = term/tparm.c;
|
terminfoinkernel = term/tparm.c;
|
||||||
|
|
255
grub-core/bus/fdt.c
Normal file
255
grub-core/bus/fdt.c
Normal file
|
@ -0,0 +1,255 @@
|
||||||
|
/*
|
||||||
|
* GRUB -- GRand Unified Bootloader
|
||||||
|
* Copyright (C) 2016 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/fdtbus.h>
|
||||||
|
#include <grub/fdt.h>
|
||||||
|
#include <grub/term.h>
|
||||||
|
|
||||||
|
static const void *dtb;
|
||||||
|
static grub_size_t root_address_cells, root_size_cells;
|
||||||
|
/* Pointer to this symbol signals invalid mapping. */
|
||||||
|
char grub_fdtbus_invalid_mapping[1];
|
||||||
|
|
||||||
|
struct grub_fdtbus_dev
|
||||||
|
{
|
||||||
|
struct grub_fdtbus_dev *next;
|
||||||
|
struct grub_fdtbus_dev *parent;
|
||||||
|
int node;
|
||||||
|
struct grub_fdtbus_driver *driver;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grub_fdtbus_dev *devs;
|
||||||
|
struct grub_fdtbus_driver *drivers;
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_compatible (struct grub_fdtbus_driver *driver,
|
||||||
|
int node)
|
||||||
|
{
|
||||||
|
grub_size_t compatible_size;
|
||||||
|
const char *compatible = grub_fdt_get_prop (dtb, node, "compatible",
|
||||||
|
&compatible_size);
|
||||||
|
const char *compatible_end = compatible + compatible_size;
|
||||||
|
while (compatible < compatible_end)
|
||||||
|
{
|
||||||
|
if (grub_strcmp (driver->compatible, compatible) == 0)
|
||||||
|
return 1;
|
||||||
|
compatible += grub_strlen (compatible) + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fdtbus_scan (struct grub_fdtbus_dev *parent)
|
||||||
|
{
|
||||||
|
int node;
|
||||||
|
for (node = grub_fdt_first_node (dtb, parent ? parent->node : 0); node >= 0;
|
||||||
|
node = grub_fdt_next_node (dtb, node))
|
||||||
|
{
|
||||||
|
struct grub_fdtbus_dev *dev;
|
||||||
|
struct grub_fdtbus_driver *driver;
|
||||||
|
dev = grub_zalloc (sizeof (*dev));
|
||||||
|
if (!dev)
|
||||||
|
{
|
||||||
|
grub_print_error ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dev->node = node;
|
||||||
|
dev->next = devs;
|
||||||
|
dev->parent = parent;
|
||||||
|
devs = dev;
|
||||||
|
FOR_LIST_ELEMENTS(driver, drivers)
|
||||||
|
if (!dev->driver && is_compatible (driver, node))
|
||||||
|
{
|
||||||
|
if (driver->attach(dev) == GRUB_ERR_NONE)
|
||||||
|
{
|
||||||
|
dev->driver = driver;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
grub_print_error ();
|
||||||
|
}
|
||||||
|
fdtbus_scan (dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grub_fdtbus_register (struct grub_fdtbus_driver *driver)
|
||||||
|
{
|
||||||
|
struct grub_fdtbus_dev *dev;
|
||||||
|
grub_list_push (GRUB_AS_LIST_P (&drivers),
|
||||||
|
GRUB_AS_LIST (driver));
|
||||||
|
for (dev = devs; dev; dev = dev->next)
|
||||||
|
if (!dev->driver && is_compatible (driver, dev->node))
|
||||||
|
{
|
||||||
|
if (driver->attach(dev) == GRUB_ERR_NONE)
|
||||||
|
dev->driver = driver;
|
||||||
|
grub_print_error ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grub_fdtbus_unregister (struct grub_fdtbus_driver *driver)
|
||||||
|
{
|
||||||
|
grub_list_remove (GRUB_AS_LIST (driver));
|
||||||
|
struct grub_fdtbus_dev *dev;
|
||||||
|
for (dev = devs; dev; dev = dev->next)
|
||||||
|
if (dev->driver == driver)
|
||||||
|
{
|
||||||
|
if (driver->detach)
|
||||||
|
driver->detach(dev);
|
||||||
|
dev->driver = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
grub_fdtbus_init (const void *dtb_in, grub_size_t size)
|
||||||
|
{
|
||||||
|
if (!dtb_in || grub_fdt_check_header (dtb_in, size) < 0)
|
||||||
|
grub_fatal ("invalid FDT");
|
||||||
|
dtb = dtb_in;
|
||||||
|
const grub_uint32_t *prop = grub_fdt_get_prop (dtb, 0, "#address-cells", 0);
|
||||||
|
if (prop)
|
||||||
|
root_address_cells = grub_be_to_cpu32 (*prop);
|
||||||
|
else
|
||||||
|
root_address_cells = 1;
|
||||||
|
|
||||||
|
prop = grub_fdt_get_prop (dtb, 0, "#size-cells", 0);
|
||||||
|
if (prop)
|
||||||
|
root_size_cells = grub_be_to_cpu32 (*prop);
|
||||||
|
else
|
||||||
|
root_size_cells = 1;
|
||||||
|
|
||||||
|
fdtbus_scan (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_address_cells (const struct grub_fdtbus_dev *dev)
|
||||||
|
{
|
||||||
|
const grub_uint32_t *prop;
|
||||||
|
if (!dev)
|
||||||
|
return root_address_cells;
|
||||||
|
prop = grub_fdt_get_prop (dtb, dev->node, "#address-cells", 0);
|
||||||
|
if (prop)
|
||||||
|
return grub_be_to_cpu32 (*prop);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_size_cells (const struct grub_fdtbus_dev *dev)
|
||||||
|
{
|
||||||
|
const grub_uint32_t *prop;
|
||||||
|
if (!dev)
|
||||||
|
return root_size_cells;
|
||||||
|
prop = grub_fdt_get_prop (dtb, dev->node, "#size-cells", 0);
|
||||||
|
if (prop)
|
||||||
|
return grub_be_to_cpu32 (*prop);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static grub_uint64_t
|
||||||
|
get64 (const grub_uint32_t *reg, grub_size_t cells)
|
||||||
|
{
|
||||||
|
grub_uint64_t val = 0;
|
||||||
|
if (cells >= 1)
|
||||||
|
val = grub_be_to_cpu32 (reg[cells - 1]);
|
||||||
|
if (cells >= 2)
|
||||||
|
val |= ((grub_uint64_t) grub_be_to_cpu32 (reg[cells - 2])) << 32;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile void *
|
||||||
|
translate (const struct grub_fdtbus_dev *dev, const grub_uint32_t *reg)
|
||||||
|
{
|
||||||
|
volatile void *ret;
|
||||||
|
const grub_uint32_t *ranges;
|
||||||
|
grub_size_t ranges_size, cells_per_mapping;
|
||||||
|
grub_size_t parent_address_cells, child_address_cells, child_size_cells;
|
||||||
|
grub_size_t nmappings, i;
|
||||||
|
if (dev == 0)
|
||||||
|
{
|
||||||
|
grub_uint64_t val;
|
||||||
|
val = get64 (reg, root_address_cells);
|
||||||
|
if (sizeof (void *) == 4 && (val >> 32))
|
||||||
|
return grub_fdtbus_invalid_mapping;
|
||||||
|
return (void *) (grub_addr_t) val;
|
||||||
|
}
|
||||||
|
ranges = grub_fdt_get_prop (dtb, dev->node, "ranges", &ranges_size);
|
||||||
|
if (!ranges)
|
||||||
|
return grub_fdtbus_invalid_mapping;
|
||||||
|
if (ranges_size == 0)
|
||||||
|
return translate (dev->parent, reg);
|
||||||
|
parent_address_cells = get_address_cells (dev->parent);
|
||||||
|
child_address_cells = get_address_cells (dev);
|
||||||
|
child_size_cells = get_size_cells (dev);
|
||||||
|
cells_per_mapping = parent_address_cells + child_address_cells + child_size_cells;
|
||||||
|
nmappings = ranges_size / 4 / cells_per_mapping;
|
||||||
|
for (i = 0; i < nmappings; i++)
|
||||||
|
{
|
||||||
|
const grub_uint32_t *child_addr = &ranges[i * cells_per_mapping];
|
||||||
|
const grub_uint32_t *parent_addr = child_addr + child_address_cells;
|
||||||
|
grub_uint64_t child_size = get64 (parent_addr + parent_address_cells, child_size_cells);
|
||||||
|
|
||||||
|
if (child_address_cells > 2 && grub_memcmp (reg, child_addr, (child_address_cells - 2) * 4) != 0)
|
||||||
|
continue;
|
||||||
|
if (get64 (reg, child_address_cells) < get64 (child_addr, child_address_cells))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
grub_uint64_t offset = get64 (reg, child_address_cells) - get64 (child_addr, child_address_cells);
|
||||||
|
if (offset >= child_size)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ret = translate (dev->parent, parent_addr);
|
||||||
|
if (grub_fdtbus_is_mapping_valid (ret))
|
||||||
|
ret = (volatile char *) ret + offset;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return grub_fdtbus_invalid_mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile void *
|
||||||
|
grub_fdtbus_map_reg (const struct grub_fdtbus_dev *dev, int regno, grub_size_t *size)
|
||||||
|
{
|
||||||
|
grub_size_t address_cells, size_cells;
|
||||||
|
address_cells = get_address_cells (dev->parent);
|
||||||
|
size_cells = get_size_cells (dev->parent);
|
||||||
|
const grub_uint32_t *reg = grub_fdt_get_prop (dtb, dev->node, "reg", 0);
|
||||||
|
if (size && size_cells)
|
||||||
|
*size = reg[(address_cells + size_cells) * regno + address_cells];
|
||||||
|
if (size && !size_cells)
|
||||||
|
*size = 0;
|
||||||
|
return translate (dev->parent, reg + (address_cells + size_cells) * regno);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
grub_fdtbus_get_name (const struct grub_fdtbus_dev *dev)
|
||||||
|
{
|
||||||
|
return grub_fdt_get_nodename (dtb, dev->node);
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *
|
||||||
|
grub_fdtbus_get_prop (const struct grub_fdtbus_dev *dev,
|
||||||
|
const char *name,
|
||||||
|
grub_uint32_t *len)
|
||||||
|
{
|
||||||
|
return grub_fdt_get_prop (dtb, dev->node, name, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *
|
||||||
|
grub_fdtbus_get_fdt (void)
|
||||||
|
{
|
||||||
|
return dtb;
|
||||||
|
}
|
|
@ -33,6 +33,7 @@
|
||||||
#include <grub/symbol.h>
|
#include <grub/symbol.h>
|
||||||
#include <grub/video.h>
|
#include <grub/video.h>
|
||||||
#include <grub/coreboot/lbio.h>
|
#include <grub/coreboot/lbio.h>
|
||||||
|
#include <grub/fdtbus.h>
|
||||||
|
|
||||||
extern grub_uint8_t _start[];
|
extern grub_uint8_t _start[];
|
||||||
extern grub_uint8_t _end[];
|
extern grub_uint8_t _end[];
|
||||||
|
@ -99,6 +100,10 @@ heap_init (grub_uint64_t addr, grub_uint64_t size, grub_memory_type_t type,
|
||||||
void
|
void
|
||||||
grub_machine_init (void)
|
grub_machine_init (void)
|
||||||
{
|
{
|
||||||
|
struct grub_module_header *header;
|
||||||
|
void *dtb = 0;
|
||||||
|
grub_size_t dtb_size = 0;
|
||||||
|
|
||||||
modend = grub_modules_get_end ();
|
modend = grub_modules_get_end ();
|
||||||
|
|
||||||
grub_video_coreboot_fb_early_init ();
|
grub_video_coreboot_fb_early_init ();
|
||||||
|
@ -112,6 +117,21 @@ grub_machine_init (void)
|
||||||
grub_font_init ();
|
grub_font_init ();
|
||||||
grub_gfxterm_init ();
|
grub_gfxterm_init ();
|
||||||
|
|
||||||
|
FOR_MODULES (header)
|
||||||
|
if (header->type == OBJ_TYPE_DTB)
|
||||||
|
{
|
||||||
|
char *dtb_orig_addr, *dtb_copy;
|
||||||
|
dtb_orig_addr = (char *) header + sizeof (struct grub_module_header);
|
||||||
|
|
||||||
|
dtb_size = header->size - sizeof (struct grub_module_header);
|
||||||
|
dtb = dtb_copy = grub_malloc (dtb_size);
|
||||||
|
grub_memmove (dtb_copy, dtb_orig_addr, dtb_size);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!dtb)
|
||||||
|
grub_fatal ("No DTB found");
|
||||||
|
grub_fdtbus_init (dtb, dtb_size);
|
||||||
|
|
||||||
grub_machine_timer_init ();
|
grub_machine_timer_init ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,13 +102,13 @@ static grub_uint32_t *get_next_node (const void *fdt, char *node_name)
|
||||||
static int get_mem_rsvmap_size (const void *fdt)
|
static int get_mem_rsvmap_size (const void *fdt)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
grub_uint64_t *ptr = (void *) ((grub_addr_t) fdt
|
grub_unaligned_uint64_t *ptr = (void *) ((grub_addr_t) fdt
|
||||||
+ grub_fdt_get_off_mem_rsvmap (fdt));
|
+ grub_fdt_get_off_mem_rsvmap (fdt));
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
size += 2 * sizeof(*ptr);
|
size += 2 * sizeof(*ptr);
|
||||||
if (!*ptr && !*(ptr + 1))
|
if (!ptr[0].val && !ptr[1].val)
|
||||||
return size;
|
return size;
|
||||||
ptr += 2;
|
ptr += 2;
|
||||||
} while ((grub_addr_t) ptr <= (grub_addr_t) fdt + grub_fdt_get_totalsize (fdt)
|
} while ((grub_addr_t) ptr <= (grub_addr_t) fdt + grub_fdt_get_totalsize (fdt)
|
||||||
|
@ -229,7 +229,7 @@ static int rearrange_blocks (void *fdt, unsigned int clearance)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static grub_uint32_t *find_prop (void *fdt, unsigned int nodeoffset,
|
static grub_uint32_t *find_prop (const void *fdt, unsigned int nodeoffset,
|
||||||
const char *name)
|
const char *name)
|
||||||
{
|
{
|
||||||
grub_uint32_t *prop = (void *) ((grub_addr_t) fdt
|
grub_uint32_t *prop = (void *) ((grub_addr_t) fdt
|
||||||
|
@ -268,9 +268,9 @@ static grub_uint32_t *find_prop (void *fdt, unsigned int nodeoffset,
|
||||||
the size allocated for the FDT; if this function is called before the other
|
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
|
functions in this file and returns success, the other functions are
|
||||||
guaranteed not to access memory locations outside the allocated memory. */
|
guaranteed not to access memory locations outside the allocated memory. */
|
||||||
int grub_fdt_check_header_nosize (void *fdt)
|
int grub_fdt_check_header_nosize (const void *fdt)
|
||||||
{
|
{
|
||||||
if (((grub_addr_t) fdt & 0x7) || (grub_fdt_get_magic (fdt) != FDT_MAGIC)
|
if (((grub_addr_t) fdt & 0x3) || (grub_fdt_get_magic (fdt) != FDT_MAGIC)
|
||||||
|| (grub_fdt_get_version (fdt) < FDT_SUPPORTED_VERSION)
|
|| (grub_fdt_get_version (fdt) < FDT_SUPPORTED_VERSION)
|
||||||
|| (grub_fdt_get_last_comp_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_off_dt_struct (fdt) & 0x00000003)
|
||||||
|
@ -286,7 +286,7 @@ int grub_fdt_check_header_nosize (void *fdt)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int grub_fdt_check_header (void *fdt, unsigned int size)
|
int grub_fdt_check_header (const void *fdt, unsigned int size)
|
||||||
{
|
{
|
||||||
if (size < sizeof (grub_fdt_header_t)
|
if (size < sizeof (grub_fdt_header_t)
|
||||||
|| (grub_fdt_get_totalsize (fdt) > size)
|
|| (grub_fdt_get_totalsize (fdt) > size)
|
||||||
|
@ -295,41 +295,29 @@ int grub_fdt_check_header (void *fdt, unsigned int size)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Find a direct sub-node of a given parent node. */
|
static const grub_uint32_t *
|
||||||
int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
|
advance_token (const void *fdt, const grub_uint32_t *token, const grub_uint32_t *end, int skip_current)
|
||||||
const char *name)
|
|
||||||
{
|
{
|
||||||
grub_uint32_t *token, *end;
|
for (; token < end; skip_current = 0)
|
||||||
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);
|
|
||||||
if ((token >= end) || (grub_be_to_cpu32(*token) != FDT_BEGIN_NODE))
|
|
||||||
return -1;
|
|
||||||
SKIP_NODE_NAME(node_name, token, end);
|
|
||||||
while (token < end)
|
|
||||||
{
|
{
|
||||||
switch (grub_be_to_cpu32 (*token))
|
switch (grub_be_to_cpu32 (*token))
|
||||||
{
|
{
|
||||||
case FDT_BEGIN_NODE:
|
case FDT_BEGIN_NODE:
|
||||||
node_name = (char *) (token + 1);
|
if (skip_current)
|
||||||
if (node_name + grub_strlen (name) >= (char *) end)
|
{
|
||||||
return -1;
|
token = get_next_node (fdt, (char *) (token + 1));
|
||||||
if (!grub_strcmp (node_name, name))
|
continue;
|
||||||
return (int) ((grub_addr_t) token - (grub_addr_t) fdt
|
}
|
||||||
- grub_fdt_get_off_dt_struct (fdt));
|
char *ptr;
|
||||||
token = get_next_node (fdt, node_name);
|
for (ptr = (char *) (token + 1); *ptr && ptr < (char *) end; ptr++);
|
||||||
if (!token)
|
if (ptr >= (char *) end)
|
||||||
return -1;
|
return 0;
|
||||||
break;
|
return token;
|
||||||
case FDT_PROP:
|
case FDT_PROP:
|
||||||
/* Skip property token and following data (len, nameoff and property
|
/* Skip property token and following data (len, nameoff and property
|
||||||
value). */
|
value). */
|
||||||
if (token >= end - 1)
|
if (token >= end - 1)
|
||||||
return -1;
|
return 0;
|
||||||
token += prop_entry_size(grub_be_to_cpu32(*(token + 1)))
|
token += prop_entry_size(grub_be_to_cpu32(*(token + 1)))
|
||||||
/ sizeof(*token);
|
/ sizeof(*token);
|
||||||
break;
|
break;
|
||||||
|
@ -337,10 +325,74 @@ int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
|
||||||
token++;
|
token++;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int grub_fdt_next_node (const void *fdt, unsigned int currentoffset)
|
||||||
|
{
|
||||||
|
const grub_uint32_t *token = (const grub_uint32_t *) fdt + (currentoffset + grub_fdt_get_off_dt_struct (fdt)) / 4;
|
||||||
|
token = advance_token (fdt, token, (const void *) struct_end (fdt), 1);
|
||||||
|
if (!token)
|
||||||
return -1;
|
return -1;
|
||||||
|
return (int) ((grub_addr_t) token - (grub_addr_t) fdt
|
||||||
|
- grub_fdt_get_off_dt_struct (fdt));
|
||||||
|
}
|
||||||
|
|
||||||
|
int grub_fdt_first_node (const void *fdt, unsigned int parentoffset)
|
||||||
|
{
|
||||||
|
const grub_uint32_t *token, *end;
|
||||||
|
char *node_name;
|
||||||
|
|
||||||
|
if (parentoffset & 0x3)
|
||||||
|
return -1;
|
||||||
|
token = (const void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt)
|
||||||
|
+ parentoffset);
|
||||||
|
end = (const void *) struct_end (fdt);
|
||||||
|
if ((token >= end) || (grub_be_to_cpu32(*token) != FDT_BEGIN_NODE))
|
||||||
|
return -1;
|
||||||
|
SKIP_NODE_NAME(node_name, token, end);
|
||||||
|
token = advance_token (fdt, token, end, 0);
|
||||||
|
if (!token)
|
||||||
|
return -1;
|
||||||
|
return (int) ((grub_addr_t) token - (grub_addr_t) fdt
|
||||||
|
- grub_fdt_get_off_dt_struct (fdt));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a direct sub-node of a given parent node. */
|
||||||
|
int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
const grub_uint32_t *token, *end;
|
||||||
|
const char *node_name;
|
||||||
|
int skip_current = 0;
|
||||||
|
|
||||||
|
if (parentoffset & 0x3)
|
||||||
|
return -1;
|
||||||
|
token = (const void *) ((grub_addr_t) fdt + grub_fdt_get_off_dt_struct(fdt)
|
||||||
|
+ parentoffset);
|
||||||
|
end = (const void *) struct_end (fdt);
|
||||||
|
if ((token >= end) || (grub_be_to_cpu32(*token) != FDT_BEGIN_NODE))
|
||||||
|
return -1;
|
||||||
|
SKIP_NODE_NAME(node_name, token, end);
|
||||||
|
while (1) {
|
||||||
|
token = advance_token (fdt, token, end, skip_current);
|
||||||
|
if (!token)
|
||||||
|
return -1;
|
||||||
|
skip_current = 1;
|
||||||
|
node_name = (const char *) token + 4;
|
||||||
|
if (grub_strcmp (node_name, name) == 0)
|
||||||
|
return (int) ((grub_addr_t) token - (grub_addr_t) fdt
|
||||||
|
- grub_fdt_get_off_dt_struct (fdt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
grub_fdt_get_nodename (const void *fdt, unsigned int nodeoffset)
|
||||||
|
{
|
||||||
|
return (const char *) fdt + grub_fdt_get_off_dt_struct(fdt) + nodeoffset + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset,
|
int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset,
|
||||||
|
@ -359,6 +411,24 @@ int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset,
|
||||||
return add_subnode (fdt, parentoffset, name);
|
return add_subnode (fdt, parentoffset, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const void *
|
||||||
|
grub_fdt_get_prop (const void *fdt, unsigned int nodeoffset, const char *name,
|
||||||
|
grub_uint32_t *len)
|
||||||
|
{
|
||||||
|
grub_uint32_t *prop;
|
||||||
|
if ((nodeoffset >= grub_fdt_get_size_dt_struct (fdt)) || (nodeoffset & 0x3)
|
||||||
|
|| (grub_be_to_cpu32(*(grub_uint32_t *) ((grub_addr_t) fdt
|
||||||
|
+ grub_fdt_get_off_dt_struct (fdt) + nodeoffset))
|
||||||
|
!= FDT_BEGIN_NODE))
|
||||||
|
return 0;
|
||||||
|
prop = find_prop (fdt, nodeoffset, name);
|
||||||
|
if (!prop)
|
||||||
|
return 0;
|
||||||
|
if (len)
|
||||||
|
*len = grub_be_to_cpu32 (*(prop + 1));
|
||||||
|
return prop + 3;
|
||||||
|
}
|
||||||
|
|
||||||
int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
|
int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
|
||||||
const void *val, grub_uint32_t len)
|
const void *val, grub_uint32_t len)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#define GRUB_FDT_HEADER 1
|
#define GRUB_FDT_HEADER 1
|
||||||
|
|
||||||
#include <grub/types.h>
|
#include <grub/types.h>
|
||||||
|
#include <grub/symbol.h>
|
||||||
|
|
||||||
#define FDT_MAGIC 0xD00DFEED
|
#define FDT_MAGIC 0xD00DFEED
|
||||||
|
|
||||||
|
@ -95,15 +96,21 @@ struct grub_fdt_empty_tree {
|
||||||
#define grub_fdt_set_size_dt_struct(fdt, value) \
|
#define grub_fdt_set_size_dt_struct(fdt, value) \
|
||||||
grub_fdt_set_header(fdt, size_dt_struct, value)
|
grub_fdt_set_header(fdt, size_dt_struct, value)
|
||||||
|
|
||||||
int grub_fdt_create_empty_tree (void *fdt, unsigned int size);
|
int EXPORT_FUNC(grub_fdt_create_empty_tree) (void *fdt, unsigned int size);
|
||||||
int grub_fdt_check_header (void *fdt, unsigned int size);
|
int EXPORT_FUNC(grub_fdt_check_header) (const void *fdt, unsigned int size);
|
||||||
int grub_fdt_check_header_nosize (void *fdt);
|
int EXPORT_FUNC(grub_fdt_check_header_nosize) (const void *fdt);
|
||||||
int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset,
|
int EXPORT_FUNC(grub_fdt_find_subnode) (const void *fdt, unsigned int parentoffset,
|
||||||
const char *name);
|
const char *name);
|
||||||
int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset,
|
int EXPORT_FUNC(grub_fdt_first_node) (const void *fdt, unsigned int parentoffset);
|
||||||
|
int EXPORT_FUNC(grub_fdt_next_node) (const void *fdt, unsigned int currentoffset);
|
||||||
|
int EXPORT_FUNC(grub_fdt_add_subnode) (void *fdt, unsigned int parentoffset,
|
||||||
const char *name);
|
const char *name);
|
||||||
|
const char *
|
||||||
|
EXPORT_FUNC(grub_fdt_get_nodename) (const void *fdt, unsigned int nodeoffset);
|
||||||
|
const void *EXPORT_FUNC(grub_fdt_get_prop) (const void *fdt, unsigned int nodeoffset, const char *name,
|
||||||
|
grub_uint32_t *len);
|
||||||
|
|
||||||
int grub_fdt_set_prop (void *fdt, unsigned int nodeoffset, const char *name,
|
int EXPORT_FUNC(grub_fdt_set_prop) (void *fdt, unsigned int nodeoffset, const char *name,
|
||||||
const void *val, grub_uint32_t len);
|
const void *val, grub_uint32_t len);
|
||||||
#define grub_fdt_set_prop32(fdt, nodeoffset, name, val) \
|
#define grub_fdt_set_prop32(fdt, nodeoffset, name, val) \
|
||||||
({ \
|
({ \
|
||||||
|
|
73
include/grub/fdtbus.h
Normal file
73
include/grub/fdtbus.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* GRUB -- GRand Unified Bootloader
|
||||||
|
* Copyright (C) 2016 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_FDTBUS_HEADER
|
||||||
|
#define GRUB_FDTBUS_HEADER 1
|
||||||
|
|
||||||
|
#include <grub/fdt.h>
|
||||||
|
#include <grub/err.h>
|
||||||
|
|
||||||
|
struct grub_fdtbus_dev;
|
||||||
|
|
||||||
|
struct grub_fdtbus_driver
|
||||||
|
{
|
||||||
|
struct grub_fdtbus_driver *next;
|
||||||
|
struct grub_fdtbus_driver **prev;
|
||||||
|
|
||||||
|
const char *compatible;
|
||||||
|
|
||||||
|
grub_err_t (*attach) (const struct grub_fdtbus_dev *dev);
|
||||||
|
void (*detach) (const struct grub_fdtbus_dev *dev);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern char EXPORT_VAR(grub_fdtbus_invalid_mapping)[1];
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
grub_fdtbus_is_mapping_valid (volatile void *m)
|
||||||
|
{
|
||||||
|
return m != grub_fdtbus_invalid_mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile void *
|
||||||
|
EXPORT_FUNC(grub_fdtbus_map_reg) (const struct grub_fdtbus_dev *dev, int reg, grub_size_t *size);
|
||||||
|
|
||||||
|
const void *
|
||||||
|
EXPORT_FUNC(grub_fdtbus_get_fdt) (void);
|
||||||
|
|
||||||
|
const char *
|
||||||
|
EXPORT_FUNC(grub_fdtbus_get_name) (const struct grub_fdtbus_dev *dev);
|
||||||
|
|
||||||
|
const void *
|
||||||
|
EXPORT_FUNC(grub_fdtbus_get_prop) (const struct grub_fdtbus_dev *dev,
|
||||||
|
const char *name,
|
||||||
|
grub_uint32_t *len);
|
||||||
|
|
||||||
|
void
|
||||||
|
EXPORT_FUNC(grub_fdtbus_register) (struct grub_fdtbus_driver *driver);
|
||||||
|
|
||||||
|
void
|
||||||
|
EXPORT_FUNC(grub_fdtbus_unregister) (struct grub_fdtbus_driver *driver);
|
||||||
|
|
||||||
|
/* Must be called before any register(). */
|
||||||
|
/* dtb is assumed to be unfreeable and must remain
|
||||||
|
valid for lifetime of GRUB.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
grub_fdtbus_init (const void *dtb, grub_size_t size);
|
||||||
|
|
||||||
|
#endif
|
|
@ -28,7 +28,8 @@ enum
|
||||||
OBJ_TYPE_MEMDISK,
|
OBJ_TYPE_MEMDISK,
|
||||||
OBJ_TYPE_CONFIG,
|
OBJ_TYPE_CONFIG,
|
||||||
OBJ_TYPE_PREFIX,
|
OBJ_TYPE_PREFIX,
|
||||||
OBJ_TYPE_PUBKEY
|
OBJ_TYPE_PUBKEY,
|
||||||
|
OBJ_TYPE_DTB
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The module header. */
|
/* The module header. */
|
||||||
|
|
|
@ -176,7 +176,7 @@ grub_install_generate_image (const char *dir, const char *prefix,
|
||||||
char *config_path,
|
char *config_path,
|
||||||
const struct grub_install_image_target_desc *image_target,
|
const struct grub_install_image_target_desc *image_target,
|
||||||
int note,
|
int note,
|
||||||
grub_compression_t comp);
|
grub_compression_t comp, const char *dtb_file);
|
||||||
|
|
||||||
const struct grub_install_image_target_desc *
|
const struct grub_install_image_target_desc *
|
||||||
grub_install_get_image_target (const char *arg);
|
grub_install_get_image_target (const char *arg);
|
||||||
|
|
|
@ -499,7 +499,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
|
||||||
grub_install_generate_image (dir, prefix, fp, outname,
|
grub_install_generate_image (dir, prefix, fp, outname,
|
||||||
modules.entries, memdisk_path,
|
modules.entries, memdisk_path,
|
||||||
pubkeys, npubkeys, config_path, tgt,
|
pubkeys, npubkeys, config_path, tgt,
|
||||||
note, compression);
|
note, compression, 0);
|
||||||
while (dc--)
|
while (dc--)
|
||||||
grub_install_pop_module ();
|
grub_install_pop_module ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ static struct argp_option options[] = {
|
||||||
N_("embed FILE as a memdisk image\n"
|
N_("embed FILE as a memdisk image\n"
|
||||||
"Implies `-p (memdisk)/boot/grub' and overrides any prefix supplied previously,"
|
"Implies `-p (memdisk)/boot/grub' and overrides any prefix supplied previously,"
|
||||||
" but the prefix itself can be overridden by later options"), 0},
|
" but the prefix itself can be overridden by later options"), 0},
|
||||||
|
{"dtb", 'D', N_("FILE"), 0, N_("embed FILE as a device tree (DTB)\n"), 0},
|
||||||
/* TRANSLATORS: "embed" is a verb (command description). "*/
|
/* TRANSLATORS: "embed" is a verb (command description). "*/
|
||||||
{"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0},
|
{"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0},
|
||||||
/* TRANSLATORS: "embed" is a verb (command description). "*/
|
/* TRANSLATORS: "embed" is a verb (command description). "*/
|
||||||
|
@ -117,6 +118,7 @@ struct arguments
|
||||||
char *dir;
|
char *dir;
|
||||||
char *prefix;
|
char *prefix;
|
||||||
char *memdisk;
|
char *memdisk;
|
||||||
|
char *dtb;
|
||||||
char **pubkeys;
|
char **pubkeys;
|
||||||
size_t npubkeys;
|
size_t npubkeys;
|
||||||
char *font;
|
char *font;
|
||||||
|
@ -176,6 +178,13 @@ argp_parser (int key, char *arg, struct argp_state *state)
|
||||||
arguments->prefix = xstrdup ("(memdisk)/boot/grub");
|
arguments->prefix = xstrdup ("(memdisk)/boot/grub");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'D':
|
||||||
|
if (arguments->dtb)
|
||||||
|
free (arguments->dtb);
|
||||||
|
|
||||||
|
arguments->dtb = xstrdup (arg);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'k':
|
case 'k':
|
||||||
arguments->pubkeys = xrealloc (arguments->pubkeys,
|
arguments->pubkeys = xrealloc (arguments->pubkeys,
|
||||||
sizeof (arguments->pubkeys[0])
|
sizeof (arguments->pubkeys[0])
|
||||||
|
@ -300,7 +309,7 @@ main (int argc, char *argv[])
|
||||||
arguments.memdisk, arguments.pubkeys,
|
arguments.memdisk, arguments.pubkeys,
|
||||||
arguments.npubkeys, arguments.config,
|
arguments.npubkeys, arguments.config,
|
||||||
arguments.image_target, arguments.note,
|
arguments.image_target, arguments.note,
|
||||||
arguments.comp);
|
arguments.comp, arguments.dtb);
|
||||||
|
|
||||||
grub_util_file_sync (fp);
|
grub_util_file_sync (fp);
|
||||||
fclose (fp);
|
fclose (fp);
|
||||||
|
|
|
@ -777,13 +777,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
|
||||||
char *memdisk_path, char **pubkey_paths,
|
char *memdisk_path, char **pubkey_paths,
|
||||||
size_t npubkeys, char *config_path,
|
size_t npubkeys, char *config_path,
|
||||||
const struct grub_install_image_target_desc *image_target,
|
const struct grub_install_image_target_desc *image_target,
|
||||||
int note,
|
int note, grub_compression_t comp, const char *dtb_path)
|
||||||
grub_compression_t comp)
|
|
||||||
{
|
{
|
||||||
char *kernel_img, *core_img;
|
char *kernel_img, *core_img;
|
||||||
size_t total_module_size, core_size;
|
size_t total_module_size, core_size;
|
||||||
size_t memdisk_size = 0, config_size = 0;
|
size_t memdisk_size = 0, config_size = 0;
|
||||||
size_t prefix_size = 0;
|
size_t prefix_size = 0, dtb_size = 0;
|
||||||
char *kernel_path;
|
char *kernel_path;
|
||||||
size_t offset;
|
size_t offset;
|
||||||
struct grub_util_path_list *path_list, *p;
|
struct grub_util_path_list *path_list, *p;
|
||||||
|
@ -828,6 +827,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
|
||||||
total_module_size += memdisk_size + sizeof (struct grub_module_header);
|
total_module_size += memdisk_size + sizeof (struct grub_module_header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dtb_path)
|
||||||
|
{
|
||||||
|
dtb_size = ALIGN_UP(grub_util_get_image_size (dtb_path), 4);
|
||||||
|
total_module_size += dtb_size + sizeof (struct grub_module_header);
|
||||||
|
}
|
||||||
|
|
||||||
if (config_path)
|
if (config_path)
|
||||||
{
|
{
|
||||||
config_size = ALIGN_ADDR (grub_util_get_image_size (config_path) + 1);
|
config_size = ALIGN_ADDR (grub_util_get_image_size (config_path) + 1);
|
||||||
|
@ -950,6 +955,19 @@ grub_install_generate_image (const char *dir, const char *prefix,
|
||||||
offset += memdisk_size;
|
offset += memdisk_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dtb_path)
|
||||||
|
{
|
||||||
|
struct grub_module_header *header;
|
||||||
|
|
||||||
|
header = (struct grub_module_header *) (kernel_img + offset);
|
||||||
|
header->type = grub_host_to_target32 (OBJ_TYPE_DTB);
|
||||||
|
header->size = grub_host_to_target32 (dtb_size + sizeof (*header));
|
||||||
|
offset += sizeof (*header);
|
||||||
|
|
||||||
|
grub_util_load_image (dtb_path, kernel_img + offset);
|
||||||
|
offset += dtb_size;
|
||||||
|
}
|
||||||
|
|
||||||
if (config_path)
|
if (config_path)
|
||||||
{
|
{
|
||||||
struct grub_module_header *header;
|
struct grub_module_header *header;
|
||||||
|
|
Loading…
Reference in a new issue