From fcbb723d4b1f7ad4687191562621dd0eb25f4d9e Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Mon, 8 May 2017 21:19:59 +0200 Subject: [PATCH 020/198] Add support for device-tree-based drivers. --- conf/Makefile.common | 4 +- grub-core/Makefile.am | 10 ++ grub-core/Makefile.core.def | 2 + grub-core/bus/fdt.c | 255 +++++++++++++++++++++++++++++++++++++ grub-core/kern/arm/coreboot/init.c | 20 +++ grub-core/lib/fdt.c | 142 +++++++++++++++------ include/grub/fdt.h | 23 ++-- include/grub/fdtbus.h | 73 +++++++++++ include/grub/kernel.h | 3 +- include/grub/util/install.h | 2 +- util/grub-install-common.c | 2 +- util/grub-mkimage.c | 11 +- util/mkimage.c | 24 +++- 13 files changed, 519 insertions(+), 52 deletions(-) create mode 100644 grub-core/bus/fdt.c create mode 100644 include/grub/fdtbus.h diff --git a/conf/Makefile.common b/conf/Makefile.common index 11296b550..311da61c6 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -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_extcmd(...)=EXTCOMMAND_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_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 diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index bec058554..fc6ca3051 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -368,6 +368,16 @@ terminal.lst: $(MARKER_FILES) platform_DATA += 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) (for pp in $^; do \ b=`basename $$pp .marker`; \ diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 411dca46b..77d0b019e 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -158,6 +158,8 @@ kernel = { arm_coreboot = kern/arm/coreboot/init.c; arm_coreboot = kern/arm/coreboot/timer.c; arm_coreboot = kern/arm/coreboot/coreboot.S; + arm_coreboot = lib/fdt.c; + arm_coreboot = bus/fdt.c; terminfoinkernel = term/terminfo.c; terminfoinkernel = term/tparm.c; diff --git a/grub-core/bus/fdt.c b/grub-core/bus/fdt.c new file mode 100644 index 000000000..6fb077000 --- /dev/null +++ b/grub-core/bus/fdt.c @@ -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 . + */ + +#include +#include +#include + +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; +} diff --git a/grub-core/kern/arm/coreboot/init.c b/grub-core/kern/arm/coreboot/init.c index 51ecaceb0..aec75c672 100644 --- a/grub-core/kern/arm/coreboot/init.c +++ b/grub-core/kern/arm/coreboot/init.c @@ -33,6 +33,7 @@ #include #include #include +#include extern grub_uint8_t _start[]; 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 grub_machine_init (void) { + struct grub_module_header *header; + void *dtb = 0; + grub_size_t dtb_size = 0; + modend = grub_modules_get_end (); grub_video_coreboot_fb_early_init (); @@ -112,6 +117,21 @@ grub_machine_init (void) grub_font_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 (); } diff --git a/grub-core/lib/fdt.c b/grub-core/lib/fdt.c index b5d520f20..bdc630244 100644 --- a/grub-core/lib/fdt.c +++ b/grub-core/lib/fdt.c @@ -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) { int size = 0; - grub_uint64_t *ptr = (void *) ((grub_addr_t) fdt - + grub_fdt_get_off_mem_rsvmap (fdt)); + grub_unaligned_uint64_t *ptr = (void *) ((grub_addr_t) fdt + + grub_fdt_get_off_mem_rsvmap (fdt)); do { size += 2 * sizeof(*ptr); - if (!*ptr && !*(ptr + 1)) + if (!ptr[0].val && !ptr[1].val) return size; ptr += 2; } 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; } -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) { 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 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_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_last_comp_version (fdt) > FDT_SUPPORTED_VERSION) || (grub_fdt_get_off_dt_struct (fdt) & 0x00000003) @@ -286,7 +286,7 @@ int grub_fdt_check_header_nosize (void *fdt) 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) || (grub_fdt_get_totalsize (fdt) > size) @@ -295,41 +295,29 @@ int grub_fdt_check_header (void *fdt, unsigned int size) 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) +static const grub_uint32_t * +advance_token (const void *fdt, const grub_uint32_t *token, const grub_uint32_t *end, int skip_current) { - 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); - if ((token >= end) || (grub_be_to_cpu32(*token) != FDT_BEGIN_NODE)) - return -1; - SKIP_NODE_NAME(node_name, token, end); - while (token < end) + for (; token < end; skip_current = 0) { - switch (grub_be_to_cpu32(*token)) + 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 - (grub_addr_t) fdt - - grub_fdt_get_off_dt_struct (fdt)); - token = get_next_node (fdt, node_name); - if (!token) - return -1; - break; + if (skip_current) + { + token = get_next_node (fdt, (char *) (token + 1)); + continue; + } + char *ptr; + for (ptr = (char *) (token + 1); *ptr && ptr < (char *) end; ptr++); + if (ptr >= (char *) end) + return 0; + return token; case FDT_PROP: /* Skip property token and following data (len, nameoff and property value). */ if (token >= end - 1) - return -1; + return 0; token += prop_entry_size(grub_be_to_cpu32(*(token + 1))) / sizeof(*token); break; @@ -337,10 +325,74 @@ int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset, token++; break; default: - return -1; + return 0; } } - return -1; + 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 (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, @@ -359,6 +411,24 @@ int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset, 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, const void *val, grub_uint32_t len) { diff --git a/include/grub/fdt.h b/include/grub/fdt.h index fdfca75bf..75525fa31 100644 --- a/include/grub/fdt.h +++ b/include/grub/fdt.h @@ -20,6 +20,7 @@ #define GRUB_FDT_HEADER 1 #include +#include #define FDT_MAGIC 0xD00DFEED @@ -95,16 +96,22 @@ struct grub_fdt_empty_tree { #define grub_fdt_set_size_dt_struct(fdt, value) \ grub_fdt_set_header(fdt, size_dt_struct, value) -int grub_fdt_create_empty_tree (void *fdt, unsigned int size); -int grub_fdt_check_header (void *fdt, unsigned int size); -int grub_fdt_check_header_nosize (void *fdt); -int grub_fdt_find_subnode (const void *fdt, unsigned int parentoffset, - const char *name); -int grub_fdt_add_subnode (void *fdt, unsigned int parentoffset, +int EXPORT_FUNC(grub_fdt_create_empty_tree) (void *fdt, unsigned int size); +int EXPORT_FUNC(grub_fdt_check_header) (const void *fdt, unsigned int size); +int EXPORT_FUNC(grub_fdt_check_header_nosize) (const void *fdt); +int EXPORT_FUNC(grub_fdt_find_subnode) (const void *fdt, unsigned int parentoffset, + const char *name); +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 * +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, - const void *val, grub_uint32_t len); +int EXPORT_FUNC(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); \ diff --git a/include/grub/fdtbus.h b/include/grub/fdtbus.h new file mode 100644 index 000000000..985837e55 --- /dev/null +++ b/include/grub/fdtbus.h @@ -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 . + */ + +#ifndef GRUB_FDTBUS_HEADER +#define GRUB_FDTBUS_HEADER 1 + +#include +#include + +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 diff --git a/include/grub/kernel.h b/include/grub/kernel.h index 20ddf2da2..ecd88ca72 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -28,7 +28,8 @@ enum OBJ_TYPE_MEMDISK, OBJ_TYPE_CONFIG, OBJ_TYPE_PREFIX, - OBJ_TYPE_PUBKEY + OBJ_TYPE_PUBKEY, + OBJ_TYPE_DTB }; /* The module header. */ diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 5ca4811cd..6abd288c3 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -176,7 +176,7 @@ grub_install_generate_image (const char *dir, const char *prefix, char *config_path, const struct grub_install_image_target_desc *image_target, int note, - grub_compression_t comp); + grub_compression_t comp, const char *dtb_file); const struct grub_install_image_target_desc * grub_install_get_image_target (const char *arg); diff --git a/util/grub-install-common.c b/util/grub-install-common.c index 452b230da..8539ff348 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -499,7 +499,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, grub_install_generate_image (dir, prefix, fp, outname, modules.entries, memdisk_path, pubkeys, npubkeys, config_path, tgt, - note, compression); + note, compression, 0); while (dc--) grub_install_pop_module (); } diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index aba19d21b..98d24cc06 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -71,6 +71,7 @@ static struct argp_option options[] = { N_("embed FILE as a memdisk image\n" "Implies `-p (memdisk)/boot/grub' and overrides any prefix supplied previously," " 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). "*/ {"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0}, /* TRANSLATORS: "embed" is a verb (command description). "*/ @@ -117,6 +118,7 @@ struct arguments char *dir; char *prefix; char *memdisk; + char *dtb; char **pubkeys; size_t npubkeys; char *font; @@ -176,6 +178,13 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->prefix = xstrdup ("(memdisk)/boot/grub"); break; + case 'D': + if (arguments->dtb) + free (arguments->dtb); + + arguments->dtb = xstrdup (arg); + break; + case 'k': arguments->pubkeys = xrealloc (arguments->pubkeys, sizeof (arguments->pubkeys[0]) @@ -300,7 +309,7 @@ main (int argc, char *argv[]) arguments.memdisk, arguments.pubkeys, arguments.npubkeys, arguments.config, arguments.image_target, arguments.note, - arguments.comp); + arguments.comp, arguments.dtb); grub_util_file_sync (fp); fclose (fp); diff --git a/util/mkimage.c b/util/mkimage.c index 6aa77ed73..e22d82afa 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -777,13 +777,12 @@ grub_install_generate_image (const char *dir, const char *prefix, char *memdisk_path, char **pubkey_paths, size_t npubkeys, char *config_path, const struct grub_install_image_target_desc *image_target, - int note, - grub_compression_t comp) + int note, grub_compression_t comp, const char *dtb_path) { char *kernel_img, *core_img; size_t total_module_size, core_size; size_t memdisk_size = 0, config_size = 0; - size_t prefix_size = 0; + size_t prefix_size = 0, dtb_size = 0; char *kernel_path; size_t offset; 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); } + 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) { 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; } + 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) { struct grub_module_header *header; -- 2.14.3