Add support for device-tree-based drivers.

This commit is contained in:
Vladimir Serbinenko 2017-05-08 21:19:59 +02:00 committed by Vincent Batts
parent aa7585d04b
commit 1895c3806b
13 changed files with 519 additions and 52 deletions

View file

@ -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

View file

@ -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`; \

View file

@ -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
View 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;
}

View file

@ -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 ();
} }

View file

@ -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)
{ {

View file

@ -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
View 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

View file

@ -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. */

View file

@ -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);

View file

@ -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 ();
} }

View file

@ -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);

View file

@ -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;