aboutsummaryrefslogtreecommitdiff
path: root/drivers/of/fdt.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/of/fdt.c')
-rw-r--r--drivers/of/fdt.c452
1 files changed, 217 insertions, 235 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 758b4f8b30b7..69f78cf203e5 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -15,9 +15,12 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/sizes.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/slab.h>
+#include <linux/libfdt.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */
#ifdef CONFIG_PPC
@@ -26,54 +29,6 @@
#include <asm/page.h>
-char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)
-{
- return ((char *)blob) +
- be32_to_cpu(blob->off_dt_strings) + offset;
-}
-
-/**
- * of_fdt_get_property - Given a node in the given flat blob, return
- * the property ptr
- */
-void *of_fdt_get_property(struct boot_param_header *blob,
- unsigned long node, const char *name,
- unsigned long *size)
-{
- unsigned long p = node;
-
- do {
- u32 tag = be32_to_cpup((__be32 *)p);
- u32 sz, noff;
- const char *nstr;
-
- p += 4;
- if (tag == OF_DT_NOP)
- continue;
- if (tag != OF_DT_PROP)
- return NULL;
-
- sz = be32_to_cpup((__be32 *)p);
- noff = be32_to_cpup((__be32 *)(p + 4));
- p += 8;
- if (be32_to_cpu(blob->version) < 0x10)
- p = ALIGN(p, sz >= 8 ? 8 : 4);
-
- nstr = of_fdt_get_string(blob, noff);
- if (nstr == NULL) {
- pr_warning("Can't find property index name !\n");
- return NULL;
- }
- if (strcmp(name, nstr) == 0) {
- if (size)
- *size = sz;
- return (void *)p;
- }
- p += sz;
- p = ALIGN(p, 4);
- } while (1);
-}
-
/**
* of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list
@@ -88,9 +43,10 @@ int of_fdt_is_compatible(struct boot_param_header *blob,
unsigned long node, const char *compat)
{
const char *cp;
- unsigned long cplen, l, score = 0;
+ int cplen;
+ unsigned long l, score = 0;
- cp = of_fdt_get_property(blob, node, "compatible", &cplen);
+ cp = fdt_getprop(blob, node, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
@@ -149,28 +105,27 @@ static void *unflatten_dt_alloc(void **mem, unsigned long size,
*/
static void * unflatten_dt_node(struct boot_param_header *blob,
void *mem,
- void **p,
+ int *poffset,
struct device_node *dad,
struct device_node ***allnextpp,
unsigned long fpsize)
{
+ const __be32 *p;
struct device_node *np;
struct property *pp, **prev_pp = NULL;
- char *pathp;
- u32 tag;
+ const char *pathp;
unsigned int l, allocl;
+ static int depth = 0;
+ int old_depth;
+ int offset;
int has_name = 0;
int new_format = 0;
- tag = be32_to_cpup(*p);
- if (tag != OF_DT_BEGIN_NODE) {
- pr_err("Weird tag at start of node: %x\n", tag);
+ pathp = fdt_get_name(blob, *poffset, &l);
+ if (!pathp)
return mem;
- }
- *p += 4;
- pathp = *p;
- l = allocl = strlen(pathp) + 1;
- *p = PTR_ALIGN(*p + l, 4);
+
+ allocl = l++;
/* version 0x10 has a more compact unit name here instead of the full
* path. we accumulate the full path size using "fpsize", we'll rebuild
@@ -188,7 +143,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
fpsize = 1;
allocl = 2;
l = 1;
- *pathp = '\0';
+ pathp = "";
} else {
/* account for '/' and path size minus terminal 0
* already in 'l'
@@ -202,6 +157,7 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
__alignof__(struct device_node));
if (allnextpp) {
char *fn;
+ of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
if (new_format) {
/* rebuild full path for new format */
@@ -232,35 +188,25 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
dad->next->sibling = np;
dad->next = np;
}
- kref_init(&np->kref);
}
/* process properties */
- while (1) {
- u32 sz, noff;
- char *pname;
-
- tag = be32_to_cpup(*p);
- if (tag == OF_DT_NOP) {
- *p += 4;
- continue;
- }
- if (tag != OF_DT_PROP)
+ for (offset = fdt_first_property_offset(blob, *poffset);
+ (offset >= 0);
+ (offset = fdt_next_property_offset(blob, offset))) {
+ const char *pname;
+ u32 sz;
+
+ if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {
+ offset = -FDT_ERR_INTERNAL;
break;
- *p += 4;
- sz = be32_to_cpup(*p);
- noff = be32_to_cpup(*p + 4);
- *p += 8;
- if (be32_to_cpu(blob->version) < 0x10)
- *p = PTR_ALIGN(*p, sz >= 8 ? 8 : 4);
-
- pname = of_fdt_get_string(blob, noff);
+ }
+
if (pname == NULL) {
pr_info("Can't find property name in list !\n");
break;
}
if (strcmp(pname, "name") == 0)
has_name = 1;
- l = strlen(pname) + 1;
pp = unflatten_dt_alloc(&mem, sizeof(struct property),
__alignof__(struct property));
if (allnextpp) {
@@ -272,26 +218,25 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
if ((strcmp(pname, "phandle") == 0) ||
(strcmp(pname, "linux,phandle") == 0)) {
if (np->phandle == 0)
- np->phandle = be32_to_cpup((__be32*)*p);
+ np->phandle = be32_to_cpup(p);
}
/* And we process the "ibm,phandle" property
* used in pSeries dynamic device tree
* stuff */
if (strcmp(pname, "ibm,phandle") == 0)
- np->phandle = be32_to_cpup((__be32 *)*p);
- pp->name = pname;
+ np->phandle = be32_to_cpup(p);
+ pp->name = (char *)pname;
pp->length = sz;
- pp->value = *p;
+ pp->value = (__be32 *)p;
*prev_pp = pp;
prev_pp = &pp->next;
}
- *p = PTR_ALIGN((*p) + sz, 4);
}
/* with version 0x10 we may not have the name property, recreate
* it here from the unit name if absent
*/
if (!has_name) {
- char *p1 = pathp, *ps = pathp, *pa = NULL;
+ const char *p1 = pathp, *ps = pathp, *pa = NULL;
int sz;
while (*p1) {
@@ -328,19 +273,18 @@ static void * unflatten_dt_node(struct boot_param_header *blob,
if (!np->type)
np->type = "<NULL>";
}
- while (tag == OF_DT_BEGIN_NODE || tag == OF_DT_NOP) {
- if (tag == OF_DT_NOP)
- *p += 4;
- else
- mem = unflatten_dt_node(blob, mem, p, np, allnextpp,
- fpsize);
- tag = be32_to_cpup(*p);
- }
- if (tag != OF_DT_END_NODE) {
- pr_err("Weird tag at end of node: %x\n", tag);
- return mem;
- }
- *p += 4;
+
+ old_depth = depth;
+ *poffset = fdt_next_node(blob, *poffset, &depth);
+ if (depth < 0)
+ depth = 0;
+ while (*poffset > 0 && depth > old_depth)
+ mem = unflatten_dt_node(blob, mem, poffset, np, allnextpp,
+ fpsize);
+
+ if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)
+ pr_err("unflatten: error %d processing FDT\n", *poffset);
+
return mem;
}
@@ -361,7 +305,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
void * (*dt_alloc)(u64 size, u64 align))
{
unsigned long size;
- void *start, *mem;
+ int start;
+ void *mem;
struct device_node **allnextp = mynodes;
pr_debug(" -> unflatten_device_tree()\n");
@@ -382,7 +327,7 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
}
/* First pass, scan for size */
- start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
+ start = 0;
size = (unsigned long)unflatten_dt_node(blob, 0, &start, NULL, NULL, 0);
size = ALIGN(size, 4);
@@ -397,10 +342,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
pr_debug(" unflattening %p...\n", mem);
/* Second pass, do actual unflattening */
- start = ((void *)blob) + be32_to_cpu(blob->off_dt_struct);
+ start = 0;
unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0);
- if (be32_to_cpup(start) != OF_DT_END)
- pr_warning("Weird tag at end of tree: %08x\n", be32_to_cpup(start));
if (be32_to_cpup(mem + size) != 0xdeadbeef)
pr_warning("End of tree marker overwritten: %08x\n",
be32_to_cpup(mem + size));
@@ -440,6 +383,129 @@ struct boot_param_header *initial_boot_params;
#ifdef CONFIG_OF_EARLY_FLATTREE
/**
+ * res_mem_reserve_reg() - reserve all memory described in 'reg' property
+ */
+static int __init __reserved_mem_reserve_reg(unsigned long node,
+ const char *uname)
+{
+ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+ phys_addr_t base, size;
+ int len;
+ const __be32 *prop;
+ int nomap, first = 1;
+
+ prop = of_get_flat_dt_prop(node, "reg", &len);
+ if (!prop)
+ return -ENOENT;
+
+ if (len && len % t_len != 0) {
+ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
+ uname);
+ return -EINVAL;
+ }
+
+ nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
+
+ while (len >= t_len) {
+ base = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+ if (base && size &&
+ early_init_dt_reserve_memory_arch(base, size, nomap) == 0)
+ pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n",
+ uname, &base, (unsigned long)size / SZ_1M);
+ else
+ pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n",
+ uname, &base, (unsigned long)size / SZ_1M);
+
+ len -= t_len;
+ if (first) {
+ fdt_reserved_mem_save_node(node, uname, base, size);
+ first = 0;
+ }
+ }
+ return 0;
+}
+
+/**
+ * __reserved_mem_check_root() - check if #size-cells, #address-cells provided
+ * in /reserved-memory matches the values supported by the current implementation,
+ * also check if ranges property has been provided
+ */
+static int __init __reserved_mem_check_root(unsigned long node)
+{
+ const __be32 *prop;
+
+ prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
+ if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
+ return -EINVAL;
+
+ prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
+ if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
+ return -EINVAL;
+
+ prop = of_get_flat_dt_prop(node, "ranges", NULL);
+ if (!prop)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
+ */
+static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
+ int depth, void *data)
+{
+ static int found;
+ const char *status;
+ int err;
+
+ if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
+ if (__reserved_mem_check_root(node) != 0) {
+ pr_err("Reserved memory: unsupported node format, ignoring\n");
+ /* break scan */
+ return 1;
+ }
+ found = 1;
+ /* scan next node */
+ return 0;
+ } else if (!found) {
+ /* scan next node */
+ return 0;
+ } else if (found && depth < 2) {
+ /* scanning of /reserved-memory has been finished */
+ return 1;
+ }
+
+ status = of_get_flat_dt_prop(node, "status", NULL);
+ if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
+ return 0;
+
+ err = __reserved_mem_reserve_reg(node, uname);
+ if (err == -ENOENT && of_get_flat_dt_prop(node, "size", NULL))
+ fdt_reserved_mem_save_node(node, uname, 0, 0);
+
+ /* scan next node */
+ return 0;
+}
+
+/**
+ * early_init_fdt_scan_reserved_mem() - create reserved memory regions
+ *
+ * This function grabs memory from early allocator for device exclusive use
+ * defined in device tree structures. It should be called by arch specific code
+ * once the early allocator (i.e. memblock) has been fully activated.
+ */
+void __init early_init_fdt_scan_reserved_mem(void)
+{
+ if (!initial_boot_params)
+ return;
+
+ of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
+ fdt_init_reserved_mem();
+}
+
+/**
* of_scan_flat_dt - scan flattened tree blob and call callback on each.
* @it: callback function
* @data: context data pointer
@@ -453,47 +519,19 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
void *data),
void *data)
{
- unsigned long p = ((unsigned long)initial_boot_params) +
- be32_to_cpu(initial_boot_params->off_dt_struct);
- int rc = 0;
- int depth = -1;
-
- do {
- u32 tag = be32_to_cpup((__be32 *)p);
- const char *pathp;
-
- p += 4;
- if (tag == OF_DT_END_NODE) {
- depth--;
- continue;
- }
- if (tag == OF_DT_NOP)
- continue;
- if (tag == OF_DT_END)
- break;
- if (tag == OF_DT_PROP) {
- u32 sz = be32_to_cpup((__be32 *)p);
- p += 8;
- if (be32_to_cpu(initial_boot_params->version) < 0x10)
- p = ALIGN(p, sz >= 8 ? 8 : 4);
- p += sz;
- p = ALIGN(p, 4);
- continue;
- }
- if (tag != OF_DT_BEGIN_NODE) {
- pr_err("Invalid tag %x in flat device tree!\n", tag);
- return -EINVAL;
- }
- depth++;
- pathp = (char *)p;
- p = ALIGN(p + strlen(pathp) + 1, 4);
+ const void *blob = initial_boot_params;
+ const char *pathp;
+ int offset, rc = 0, depth = -1;
+
+ for (offset = fdt_next_node(blob, -1, &depth);
+ offset >= 0 && depth >= 0 && !rc;
+ offset = fdt_next_node(blob, offset, &depth)) {
+
+ pathp = fdt_get_name(blob, offset, NULL);
if (*pathp == '/')
pathp = kbasename(pathp);
- rc = it(p, pathp, depth, data);
- if (rc != 0)
- break;
- } while (1);
-
+ rc = it(offset, pathp, depth, data);
+ }
return rc;
}
@@ -502,14 +540,7 @@ int __init of_scan_flat_dt(int (*it)(unsigned long node,
*/
unsigned long __init of_get_flat_dt_root(void)
{
- unsigned long p = ((unsigned long)initial_boot_params) +
- be32_to_cpu(initial_boot_params->off_dt_struct);
-
- while (be32_to_cpup((__be32 *)p) == OF_DT_NOP)
- p += 4;
- BUG_ON(be32_to_cpup((__be32 *)p) != OF_DT_BEGIN_NODE);
- p += 4;
- return ALIGN(p + strlen((char *)p) + 1, 4);
+ return 0;
}
/**
@@ -518,10 +549,10 @@ unsigned long __init of_get_flat_dt_root(void)
* This function can be used within scan_flattened_dt callback to get
* access to properties
*/
-void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
- unsigned long *size)
+const void *__init of_get_flat_dt_prop(unsigned long node, const char *name,
+ int *size)
{
- return of_fdt_get_property(initial_boot_params, node, name, size);
+ return fdt_getprop(initial_boot_params, node, name, size);
}
/**
@@ -551,73 +582,6 @@ struct fdt_scan_status {
void *data;
};
-/**
- * fdt_scan_node_by_path - iterator for of_scan_flat_dt_by_path function
- */
-static int __init fdt_scan_node_by_path(unsigned long node, const char *uname,
- int depth, void *data)
-{
- struct fdt_scan_status *st = data;
-
- /*
- * if scan at the requested fdt node has been completed,
- * return -ENXIO to abort further scanning
- */
- if (depth <= st->depth)
- return -ENXIO;
-
- /* requested fdt node has been found, so call iterator function */
- if (st->found)
- return st->iterator(node, uname, depth, st->data);
-
- /* check if scanning automata is entering next level of fdt nodes */
- if (depth == st->depth + 1 &&
- strncmp(st->name, uname, st->namelen) == 0 &&
- uname[st->namelen] == 0) {
- st->depth += 1;
- if (st->name[st->namelen] == 0) {
- st->found = 1;
- } else {
- const char *next = st->name + st->namelen + 1;
- st->name = next;
- st->namelen = strcspn(next, "/");
- }
- return 0;
- }
-
- /* scan next fdt node */
- return 0;
-}
-
-/**
- * of_scan_flat_dt_by_path - scan flattened tree blob and call callback on each
- * child of the given path.
- * @path: path to start searching for children
- * @it: callback function
- * @data: context data pointer
- *
- * This function is used to scan the flattened device-tree starting from the
- * node given by path. It is used to extract information (like reserved
- * memory), which is required on ealy boot before we can unflatten the tree.
- */
-int __init of_scan_flat_dt_by_path(const char *path,
- int (*it)(unsigned long node, const char *name, int depth, void *data),
- void *data)
-{
- struct fdt_scan_status st = {path, 0, -1, 0, it, data};
- int ret = 0;
-
- if (initial_boot_params)
- ret = of_scan_flat_dt(fdt_scan_node_by_path, &st);
-
- if (!st.found)
- return -ENOENT;
- else if (ret == -ENXIO) /* scan has been completed */
- return 0;
- else
- return ret;
-}
-
const char * __init of_flat_dt_get_machine_name(void)
{
const char *name;
@@ -657,7 +621,7 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
}
if (!best_data) {
const char *prop;
- long size;
+ int size;
pr_err("\n unrecognized device tree list:\n[ ");
@@ -686,8 +650,8 @@ const void * __init of_flat_dt_match_machine(const void *default_match,
static void __init early_init_dt_check_for_initrd(unsigned long node)
{
u64 start, end;
- unsigned long len;
- __be32 *prop;
+ int len;
+ const __be32 *prop;
pr_debug("Looking for initrd properties... ");
@@ -720,7 +684,7 @@ static inline void early_init_dt_check_for_initrd(unsigned long node)
int __init early_init_dt_scan_root(unsigned long node, const char *uname,
int depth, void *data)
{
- __be32 *prop;
+ const __be32 *prop;
if (depth != 0)
return 0;
@@ -742,9 +706,9 @@ int __init early_init_dt_scan_root(unsigned long node, const char *uname,
return 1;
}
-u64 __init dt_mem_next_cell(int s, __be32 **cellp)
+u64 __init dt_mem_next_cell(int s, const __be32 **cellp)
{
- __be32 *p = *cellp;
+ const __be32 *p = *cellp;
*cellp = p + s;
return of_read_number(p, s);
@@ -756,9 +720,9 @@ u64 __init dt_mem_next_cell(int s, __be32 **cellp)
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int depth, void *data)
{
- char *type = of_get_flat_dt_prop(node, "device_type", NULL);
- __be32 *reg, *endp;
- unsigned long l;
+ const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
+ const __be32 *reg, *endp;
+ int l;
/* We are scanning "memory" nodes only */
if (type == NULL) {
@@ -779,7 +743,7 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
endp = reg + (l / sizeof(__be32));
- pr_debug("memory scan node %s, reg size %ld, data: %x %x %x %x,\n",
+ pr_debug("memory scan node %s, reg size %d, data: %x %x %x %x,\n",
uname, l, reg[0], reg[1], reg[2], reg[3]);
while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
@@ -802,8 +766,8 @@ int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
int depth, void *data)
{
- unsigned long l;
- char *p;
+ int l;
+ const char *p;
pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
@@ -856,6 +820,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
memblock_add(base, size);
}
+int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
+ phys_addr_t size, bool nomap)
+{
+ if (memblock_is_region_reserved(base, size))
+ return -EBUSY;
+ if (nomap)
+ return memblock_remove(base, size);
+ return memblock_reserve(base, size);
+}
+
/*
* called from unflatten_device_tree() to bootstrap devicetree itself
* Architectures can override this definition if memblock isn't used
@@ -864,6 +838,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align)
{
return __va(memblock_alloc(size, align));
}
+#else
+int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
+ phys_addr_t size, bool nomap)
+{
+ pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n",
+ base, size, nomap ? " (nomap)" : "");
+ return -ENOSYS;
+}
#endif
bool __init early_init_dt_scan(void *params)