Skip to content

Commit e8d9d1f

Browse files
mszyprowglikely
authored andcommitted
drivers: of: add initialization code for static reserved memory
This patch adds support for static (defined by 'reg' property) reserved memory regions declared in device tree. Memory blocks can be reliably reserved only during early boot. This must happen before the whole memory management subsystem is initialized, because we need to ensure that the given contiguous blocks are not yet allocated by kernel. Also it must happen before kernel mappings for the whole low memory are created, to ensure that there will be no mappings (for reserved blocks). Typically, all this happens before device tree structures are unflattened, so we need to get reserved memory layout directly from fdt. Based on previous code provided by Josh Cartwright <joshc@codeaurora.org> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Grant Likely <grant.likely@linaro.org>
1 parent f08ad1d commit e8d9d1f

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-0
lines changed

drivers/of/fdt.c

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <linux/module.h>
1616
#include <linux/of.h>
1717
#include <linux/of_fdt.h>
18+
#include <linux/sizes.h>
1819
#include <linux/string.h>
1920
#include <linux/errno.h>
2021
#include <linux/slab.h>
@@ -439,6 +440,118 @@ struct boot_param_header *initial_boot_params;
439440

440441
#ifdef CONFIG_OF_EARLY_FLATTREE
441442

443+
/**
444+
* res_mem_reserve_reg() - reserve all memory described in 'reg' property
445+
*/
446+
static int __init __reserved_mem_reserve_reg(unsigned long node,
447+
const char *uname)
448+
{
449+
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
450+
phys_addr_t base, size;
451+
unsigned long len;
452+
__be32 *prop;
453+
int nomap;
454+
455+
prop = of_get_flat_dt_prop(node, "reg", &len);
456+
if (!prop)
457+
return -ENOENT;
458+
459+
if (len && len % t_len != 0) {
460+
pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
461+
uname);
462+
return -EINVAL;
463+
}
464+
465+
nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL;
466+
467+
while (len >= t_len) {
468+
base = dt_mem_next_cell(dt_root_addr_cells, &prop);
469+
size = dt_mem_next_cell(dt_root_size_cells, &prop);
470+
471+
if (base && size &&
472+
early_init_dt_reserve_memory_arch(base, size, nomap) == 0)
473+
pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n",
474+
uname, &base, (unsigned long)size / SZ_1M);
475+
else
476+
pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n",
477+
uname, &base, (unsigned long)size / SZ_1M);
478+
479+
len -= t_len;
480+
}
481+
return 0;
482+
}
483+
484+
/**
485+
* __reserved_mem_check_root() - check if #size-cells, #address-cells provided
486+
* in /reserved-memory matches the values supported by the current implementation,
487+
* also check if ranges property has been provided
488+
*/
489+
static int __reserved_mem_check_root(unsigned long node)
490+
{
491+
__be32 *prop;
492+
493+
prop = of_get_flat_dt_prop(node, "#size-cells", NULL);
494+
if (!prop || be32_to_cpup(prop) != dt_root_size_cells)
495+
return -EINVAL;
496+
497+
prop = of_get_flat_dt_prop(node, "#address-cells", NULL);
498+
if (!prop || be32_to_cpup(prop) != dt_root_addr_cells)
499+
return -EINVAL;
500+
501+
prop = of_get_flat_dt_prop(node, "ranges", NULL);
502+
if (!prop)
503+
return -EINVAL;
504+
return 0;
505+
}
506+
507+
/**
508+
* fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
509+
*/
510+
static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname,
511+
int depth, void *data)
512+
{
513+
static int found;
514+
const char *status;
515+
516+
if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) {
517+
if (__reserved_mem_check_root(node) != 0) {
518+
pr_err("Reserved memory: unsupported node format, ignoring\n");
519+
/* break scan */
520+
return 1;
521+
}
522+
found = 1;
523+
/* scan next node */
524+
return 0;
525+
} else if (!found) {
526+
/* scan next node */
527+
return 0;
528+
} else if (found && depth < 2) {
529+
/* scanning of /reserved-memory has been finished */
530+
return 1;
531+
}
532+
533+
status = of_get_flat_dt_prop(node, "status", NULL);
534+
if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0)
535+
return 0;
536+
537+
__reserved_mem_reserve_reg(node, uname);
538+
539+
/* scan next node */
540+
return 0;
541+
}
542+
543+
/**
544+
* early_init_fdt_scan_reserved_mem() - create reserved memory regions
545+
*
546+
* This function grabs memory from early allocator for device exclusive use
547+
* defined in device tree structures. It should be called by arch specific code
548+
* once the early allocator (i.e. memblock) has been fully activated.
549+
*/
550+
void __init early_init_fdt_scan_reserved_mem(void)
551+
{
552+
of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
553+
}
554+
442555
/**
443556
* of_scan_flat_dt - scan flattened tree blob and call callback on each.
444557
* @it: callback function
@@ -856,6 +969,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
856969
memblock_add(base, size);
857970
}
858971

972+
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
973+
phys_addr_t size, bool nomap)
974+
{
975+
if (memblock_is_region_reserved(base, size))
976+
return -EBUSY;
977+
if (nomap)
978+
return memblock_remove(base, size);
979+
return memblock_reserve(base, size);
980+
}
981+
859982
/*
860983
* called from unflatten_device_tree() to bootstrap devicetree itself
861984
* Architectures can override this definition if memblock isn't used
@@ -864,6 +987,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align)
864987
{
865988
return __va(memblock_alloc(size, align));
866989
}
990+
#else
991+
int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base,
992+
phys_addr_t size, bool nomap)
993+
{
994+
pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n",
995+
base, size, nomap ? " (nomap)" : "");
996+
return -ENOSYS;
997+
}
867998
#endif
868999

8691000
bool __init early_init_dt_scan(void *params)

include/linux/of_fdt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname,
9898
int depth, void *data);
9999
extern int early_init_dt_scan_memory(unsigned long node, const char *uname,
100100
int depth, void *data);
101+
extern void early_init_fdt_scan_reserved_mem(void);
101102
extern void early_init_dt_add_memory_arch(u64 base, u64 size);
103+
extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size,
104+
bool no_map);
102105
extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align);
103106
extern u64 dt_mem_next_cell(int s, __be32 **cellp);
104107

@@ -118,6 +121,7 @@ extern void unflatten_and_copy_device_tree(void);
118121
extern void early_init_devtree(void *);
119122
extern void early_get_first_memblock_info(void *, phys_addr_t *);
120123
#else /* CONFIG_OF_FLATTREE */
124+
static inline void early_init_fdt_scan_reserved_mem(void) {}
121125
static inline const char *of_flat_dt_get_machine_name(void) { return NULL; }
122126
static inline void unflatten_device_tree(void) {}
123127
static inline void unflatten_and_copy_device_tree(void) {}

0 commit comments

Comments
 (0)