The reserved_mem array is used to store the data of the different
reserved memory regions specified in the DT of a device.
The array stores information such as the name, node, starting address,
and size of a reserved memory region.
The array is currently statically allocated with a size of
MAX_RESERVED_REGIONS(64). This means that any system that specifies a
number of reserved memory regions greater than MAX_RESERVED_REGIONS(64)
will not have enough space to store the information for all the regions.
Therefore, this series extends the use of a static array for
reserved_mem, and introduces a dynamically allocated array using
memblock_alloc() based on the number of reserved memory regions
specified in the DT.
Memory gotten from memblock_alloc() is only writable after paging_init()
is called, but the reserved memory regions need to be reserved before
then so that the system does not create page table mappings for them.
Reserved memory regions can be divided into 2 groups.
i) Statically-placed reserved memory regions
i.e. regions defined in the DT using the @reg property.
ii) Dynamically-placed reserved memory regions.
i.e. regions specified in the DT using the @alloc_ranges
and @size properties.
It is possible to call memblock_reserve() and memblock_mark_nomap() on
the statically-placed reserved memory regions and not need to save them
to the array until after paging_init(), but this is not possible for the
dynamically-placed reserved memory because the starting address of these
regions need to be stored somewhere after they are allocated.
Therefore, this series achieves the allocation and population of the
reserved_mem array in two steps:
1. Before paging_init()
Before paging_init() is called, iterate through the reserved_mem
nodes in the DT and do the following:
- Allocate memory for dynamically-placed reserved memory regions and
store their starting address in the static allocated reserved_mem
array.
- Call memblock_reserve() and memblock_mark_nomap() on all the
reserved memory regions as needed.
- Count the total number of reserved_mem nodes in the DT.
2. After paging_init()
After paging_init() is called:
- Allocate new memory for the reserved_mem array based on the number
of reserved memory nodes in the DT.
- Transfer all the information that was stored in the static array
into the new array.
- Store the rest of the reserved_mem regions in the new array.
i.e. the statically-placed regions.
The static array is no longer needed after this point, but there is
currently no obvious way to free the memory. Therefore, the size of the
initial static array is now defined using a config option.
Because the array is used only before paging_init() to store the
dynamically-placed reserved memory regions, the required size can vary
from device to device. Therefore, scaling it can help get some memory
savings.
A possible solution to freeing the memory for the static array will be
to mark it as __initdata. This will automatically free the memory once
the init process is done running.
The reason why this is not pursued in this series is because of
the possibility of a use-after-free.
If the dynamic allocation of the reserved_mem array fails, then future
accesses of the reserved_mem array will still be referencing the static
array. When the init process ends and the memory is freed up, any
further attempts to use the reserved_mem array will result in a
use-after-free.
Note:
- The limitation to this approach is that there is still a limit of
64 for dynamically reserved memory regions.
- Upon further review, the series might need to be split up/duplicated
for other archs.
Oreoluwa Babatunde (6):
of: reserved_mem: Change the order that reserved_mem regions are
stored
of: reserved_mem: Swicth call to unflatten_device_tree() to after
paging_init()
of: resevred_mem: Delay allocation of memory for dynamic regions
of: reserved_mem: Add code to use unflattened DT for reserved_mem
nodes
of: reserved_mem: Add code to dynamically allocate reserved_mem array
of: reserved_mem: Make MAX_RESERVED_REGIONS a config option
arch/loongarch/kernel/setup.c | 2 +-
arch/mips/kernel/setup.c | 3 +-
arch/nios2/kernel/setup.c | 4 +-
arch/openrisc/kernel/setup.c | 4 +-
arch/powerpc/kernel/setup-common.c | 3 +
arch/sh/kernel/setup.c | 5 +-
arch/um/kernel/dtb.c | 1 -
arch/um/kernel/um_arch.c | 2 +
arch/xtensa/kernel/setup.c | 4 +-
drivers/of/Kconfig | 13 +++
drivers/of/fdt.c | 39 +++++--
drivers/of/of_private.h | 6 +-
drivers/of/of_reserved_mem.c | 175 +++++++++++++++++++++--------
include/linux/of_reserved_mem.h | 8 +-
kernel/dma/coherent.c | 4 +-
kernel/dma/contiguous.c | 8 +-
kernel/dma/swiotlb.c | 10 +-
17 files changed, 205 insertions(+), 86 deletions(-)
--
2.17.1
The dynamic allocation of the reserved_mem array needs to be done after
paging_init() is called because memory allocated using memblock_alloc()
is not writeable before that.
Statically-placed reserved memory regions can wait until after
paging_init() to be stored in the array because the starting address and
size information is already specified in the DT.
Dynamically-placed regions on the other hand does not have its starting
address specified in the DT because it is allocated at runtime. Hence,
the starting address of these regions need to be stored somewhere.
The allocation of dynamically-placed regions cannot be done after
paging_init() either to avoid getting regions that are already added to
the page table mappings.
Hence, change the code to:
1. Before paging_init(), allocate and store information for the
dynamically-placed reserved memory regions.
2. After paging_init(), store the information for the statically
placed reserved memory regions in the array.
All the regions are also reserved or marked as nomap as needed before
paging_init().
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/fdt.c | 63 ++++++++++++++++++++++++++++-----
drivers/of/of_reserved_mem.c | 53 ++++++++++++---------------
include/linux/of_fdt.h | 1 +
include/linux/of_reserved_mem.h | 6 ++++
4 files changed, 84 insertions(+), 39 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index bf502ba8da95..34223c249914 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -504,7 +504,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
phys_addr_t base, size;
int len;
const __be32 *prop;
- int first = 1;
bool nomap;
prop = of_get_flat_dt_prop(node, "reg", &len);
@@ -532,10 +531,6 @@ static int __init __reserved_mem_reserve_reg(unsigned long node,
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;
}
@@ -564,7 +559,56 @@ static int __init __reserved_mem_check_root(unsigned long node)
}
/*
- * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory
+ * Save the reserved_mem reg nodes in the reserved_mem array
+ */
+void __init fdt_scan_reserved_mem_reg_nodes(void)
+
+{
+ int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
+ const void *fdt = initial_boot_params;
+ phys_addr_t base, size;
+ const __be32 *prop;
+ int node, child;
+ int len;
+
+ node = fdt_path_offset(fdt, "/reserved-memory");
+ if (node < 0) {
+ pr_err("Reserved memory: Did not find reserved-memory node\n");
+ return;
+ }
+
+ if (__reserved_mem_check_root(node) != 0) {
+ pr_err("Reserved memory: unsupported node format, ignoring\n");
+ return;
+ }
+
+ fdt_for_each_subnode(child, fdt, node) {
+ const char *uname;
+
+ prop = of_get_flat_dt_prop(child, "reg", &len);
+ if (!prop)
+ continue;
+
+ if (!of_fdt_device_is_available(fdt, child))
+ continue;
+
+ uname = fdt_get_name(fdt, child, NULL);
+ if (len && len % t_len != 0) {
+ pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n",
+ uname);
+ continue;
+ }
+
+ base = dt_mem_next_cell(dt_root_addr_cells, &prop);
+ size = dt_mem_next_cell(dt_root_size_cells, &prop);
+
+ if (size)
+ fdt_reserved_mem_save_node(child, uname, base, size);
+ }
+}
+
+/*
+ * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory.
*/
static int __init fdt_scan_reserved_mem(void)
{
@@ -591,7 +635,7 @@ static int __init fdt_scan_reserved_mem(void)
err = __reserved_mem_reserve_reg(child, uname);
if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL))
- fdt_reserved_mem_save_node(child, uname, 0, 0);
+ __reserved_mem_alloc_size(child, uname);
}
return 0;
}
@@ -645,8 +689,6 @@ void __init early_init_fdt_scan_reserved_mem(void)
break;
memblock_reserve(base, size);
}
-
- fdt_init_reserved_mem();
}
/**
@@ -1335,6 +1377,9 @@ void __init unflatten_device_tree(void)
of_alias_scan(early_init_dt_alloc_memory_arch);
unittest_unflatten_overlay_base();
+
+ /*Initialize the reserved_mem regions*/
+ fdt_init_reserved_mem();
}
/**
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 7ec94cfcbddb..8bd8bbc3aaec 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -132,8 +132,7 @@ static int __init __reserved_mem_alloc_in_range(phys_addr_t size,
* __reserved_mem_alloc_size() - allocate reserved memory described by
* 'size', 'alignment' and 'alloc-ranges' properties.
*/
-static int __init __reserved_mem_alloc_size(unsigned long node,
- const char *uname, phys_addr_t *res_base, phys_addr_t *res_size)
+int __init __reserved_mem_alloc_size(unsigned long node, const char *uname)
{
int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32);
phys_addr_t start = 0, end = 0;
@@ -212,10 +211,7 @@ static int __init __reserved_mem_alloc_size(unsigned long node,
uname, (unsigned long)(size / SZ_1M));
return -ENOMEM;
}
-
- *res_base = base;
- *res_size = size;
-
+ fdt_reserved_mem_save_node(node, uname, base, size);
return 0;
}
@@ -310,6 +306,8 @@ void __init fdt_init_reserved_mem(void)
{
int i;
+ fdt_scan_reserved_mem_reg_nodes();
+
/* check for overlapping reserved regions */
__rmem_check_for_overlap();
@@ -328,30 +326,25 @@ void __init fdt_init_reserved_mem(void)
if (prop)
rmem->phandle = of_read_number(prop, len/4);
- if (rmem->size == 0)
- err = __reserved_mem_alloc_size(node, rmem->name,
- &rmem->base, &rmem->size);
- if (err == 0) {
- err = __reserved_mem_init_node(rmem);
- if (err != 0 && err != -ENOENT) {
- pr_info("node %s compatible matching fail\n",
- rmem->name);
- if (nomap)
- memblock_clear_nomap(rmem->base, rmem->size);
- else
- memblock_phys_free(rmem->base,
- rmem->size);
- } else {
- phys_addr_t end = rmem->base + rmem->size - 1;
- bool reusable =
- (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
-
- pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
- &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
- nomap ? "nomap" : "map",
- reusable ? "reusable" : "non-reusable",
- rmem->name ? rmem->name : "unknown");
- }
+ err = __reserved_mem_init_node(rmem);
+ if (err != 0 && err != -ENOENT) {
+ pr_info("node %s compatible matching fail\n",
+ rmem->name);
+ if (nomap)
+ memblock_clear_nomap(rmem->base, rmem->size);
+ else
+ memblock_phys_free(rmem->base,
+ rmem->size);
+ } else {
+ phys_addr_t end = rmem->base + rmem->size - 1;
+ bool reusable =
+ (of_get_flat_dt_prop(node, "reusable", NULL)) != NULL;
+
+ pr_info("%pa..%pa (%lu KiB) %s %s %s\n",
+ &rmem->base, &end, (unsigned long)(rmem->size / SZ_1K),
+ nomap ? "nomap" : "map",
+ reusable ? "reusable" : "non-reusable",
+ rmem->name ? rmem->name : "unknown");
}
}
}
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h
index d69ad5bb1eb1..7b2a5d93d719 100644
--- a/include/linux/of_fdt.h
+++ b/include/linux/of_fdt.h
@@ -73,6 +73,7 @@ extern int early_init_dt_scan_root(void);
extern bool early_init_dt_scan(void *params);
extern bool early_init_dt_verify(void *params);
extern void early_init_dt_scan_nodes(void);
+extern void fdt_scan_reserved_mem_reg_nodes(void);
extern const char *of_flat_dt_get_machine_name(void);
extern const void *of_flat_dt_match_machine(const void *default_match,
diff --git a/include/linux/of_reserved_mem.h b/include/linux/of_reserved_mem.h
index 4de2a24cadc9..dc13bcd04b12 100644
--- a/include/linux/of_reserved_mem.h
+++ b/include/linux/of_reserved_mem.h
@@ -38,6 +38,7 @@ int of_reserved_mem_device_init_by_name(struct device *dev,
struct device_node *np,
const char *name);
void of_reserved_mem_device_release(struct device *dev);
+int __reserved_mem_alloc_size(unsigned long node, const char *uname);
struct reserved_mem *of_reserved_mem_lookup(struct device_node *np);
#else
@@ -60,6 +61,11 @@ static inline int of_reserved_mem_device_init_by_name(struct device *dev,
static inline void of_reserved_mem_device_release(struct device *pdev) { }
+static inline int __reserved_mem_alloc_size(unsigned long node, const char *uname)
+{
+ return -ENOSYS;
+}
+
static inline struct reserved_mem *of_reserved_mem_lookup(struct device_node *np)
{
return NULL;
--
2.17.1
The reserved_mem array is statically allocated with a size of
MAX_RESERVED_REGIONS(64). Therefore, if the number of reserved_mem
regions exceeds this size, there will not be enough space to store
all the data.
Therefore, extend the use of the static array by introducing a
dynamically allocated array based on the number of reserved memory
regions specified in the DT.
Before paging_init() runs, the static array is used to store the
dynamically-placed regions.
After paging_init(), memory is dynamically allocated for the
reserved_mem array, and all entries from the static array is copied
over to the new array, and all other statically-placed regions are
added in as well.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
drivers/of/fdt.c | 13 +++++++---
drivers/of/of_private.h | 1 +
drivers/of/of_reserved_mem.c | 48 +++++++++++++++++++++++++++++++++---
3 files changed, 55 insertions(+), 7 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 12769dd53c34..2f1eabbd6869 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -563,8 +563,8 @@ static int __init __reserved_mem_check_root(unsigned long node)
*/
static int __init fdt_scan_reserved_mem(void)
{
- int node, child;
- int dynamic_nodes_cnt = 0;
+ int node, child, err = 0;
+ int dynamic_nodes_cnt = 0, count = 0;
int dynamic_nodes[MAX_RESERVED_REGIONS];
const void *fdt = initial_boot_params;
@@ -579,7 +579,6 @@ static int __init fdt_scan_reserved_mem(void)
fdt_for_each_subnode(child, fdt, node) {
const char *uname;
- int err;
if (!of_fdt_device_is_available(fdt, child))
continue;
@@ -587,6 +586,8 @@ static int __init fdt_scan_reserved_mem(void)
uname = fdt_get_name(fdt, child, NULL);
err = __reserved_mem_reserve_reg(child, uname);
+ if (!err)
+ count++;
if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) {
dynamic_nodes[dynamic_nodes_cnt] = child;
@@ -600,8 +601,12 @@ static int __init fdt_scan_reserved_mem(void)
child = dynamic_nodes[i];
uname = fdt_get_name(fdt, child, NULL);
- __reserved_mem_alloc_size(child, uname);
+ err = __reserved_mem_alloc_size(child, uname);
+ if (!err)
+ count++;
}
+ update_reserved_mem_max_cnt(count);
+
return 0;
}
diff --git a/drivers/of/of_private.h b/drivers/of/of_private.h
index 575e2b4119e0..ef56b2ea185c 100644
--- a/drivers/of/of_private.h
+++ b/drivers/of/of_private.h
@@ -179,5 +179,6 @@ static inline struct device_node *__of_get_dma_parent(const struct device_node *
void init_reserved_mem(void);
void dt_reserved_mem_save_node(struct device_node *node, const char *uname,
phys_addr_t base, phys_addr_t size);
+void update_reserved_mem_max_cnt(int max_count);
#endif /* _LINUX_OF_PRIVATE_H */
diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c
index 2ef9edcb8c93..01cd6a571dc2 100644
--- a/drivers/of/of_reserved_mem.c
+++ b/drivers/of/of_reserved_mem.c
@@ -26,7 +26,9 @@
#include "of_private.h"
-static struct reserved_mem reserved_mem[MAX_RESERVED_REGIONS];
+static struct reserved_mem reserved_mem_array[MAX_RESERVED_REGIONS];
+static struct reserved_mem *reserved_mem = reserved_mem_array;
+static int total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
static int reserved_mem_count;
static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
@@ -54,6 +56,42 @@ static int __init early_init_dt_alloc_reserved_memory_arch(phys_addr_t size,
return err;
}
+void __init update_reserved_mem_max_cnt(int max_count)
+{
+ total_reserved_mem_cnt = max_count;
+}
+
+static int alloc_reserved_mem_array(void)
+{
+ struct reserved_mem *new_array;
+ size_t alloc_size, copy_size, memset_size;
+
+ alloc_size = array_size(total_reserved_mem_cnt, sizeof(*new_array));
+ if (alloc_size == SIZE_MAX)
+ return -1;
+
+ new_array = memblock_alloc(alloc_size, SMP_CACHE_BYTES);
+ if (!new_array)
+ return -ENOMEM;
+
+ copy_size = array_size(reserved_mem_count, sizeof(*new_array));
+ if (copy_size == SIZE_MAX)
+ goto overlow_err;
+
+ memset_size = alloc_size - copy_size;
+
+ memcpy(new_array, reserved_mem, copy_size);
+ memset(new_array + reserved_mem_count, 0, memset_size);
+
+ reserved_mem = new_array;
+ return 0;
+
+overlow_err:
+ memblock_free(new_array, alloc_size);
+ total_reserved_mem_cnt = MAX_RESERVED_REGIONS;
+ return -1;
+}
+
/*
* dt_reserved_mem_save_node() - save dt node for second pass initialization
*/
@@ -62,7 +100,7 @@ void __init dt_reserved_mem_save_node(struct device_node *node, const char *unam
{
struct reserved_mem *rmem = &reserved_mem[reserved_mem_count];
- if (reserved_mem_count == ARRAY_SIZE(reserved_mem)) {
+ if (reserved_mem_count == total_reserved_mem_cnt) {
pr_err("not enough space for all defined regions.\n");
return;
}
@@ -346,7 +384,11 @@ static void __init __rmem_check_for_overlap(void)
*/
void __init init_reserved_mem(void)
{
- int i;
+ int i, ret;
+
+ ret = alloc_reserved_mem_array();
+ if (ret)
+ pr_err("Failed to allocate memory for reserved_mem array with err: %d", ret);
scan_reserved_mem_reg_nodes();
--
2.17.1
Switch call to unflatten_device_tree() to after paging_init() on other
archs to follow new order in which the reserved_mem regions are
processed.
Signed-off-by: Oreoluwa Babatunde <[email protected]>
---
arch/loongarch/kernel/setup.c | 2 +-
arch/mips/kernel/setup.c | 3 ++-
arch/nios2/kernel/setup.c | 4 ++--
arch/openrisc/kernel/setup.c | 4 ++--
arch/powerpc/kernel/setup-common.c | 3 +++
arch/sh/kernel/setup.c | 5 ++---
arch/um/kernel/dtb.c | 1 -
arch/um/kernel/um_arch.c | 2 ++
arch/xtensa/kernel/setup.c | 4 +++-
9 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index d183a745fb85..a01051b0f9e0 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -366,7 +366,6 @@ void __init platform_init(void)
acpi_gbl_use_default_register_widths = false;
acpi_boot_table_init();
#endif
- unflatten_and_copy_device_tree();
#ifdef CONFIG_NUMA
init_numa_memory();
@@ -626,6 +625,7 @@ void __init setup_arch(char **cmdline_p)
paging_init();
+ unflatten_and_copy_device_tree();
#ifdef CONFIG_KASAN
kasan_init();
#endif
diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c
index 2d2ca024bd47..d3b6c86a8037 100644
--- a/arch/mips/kernel/setup.c
+++ b/arch/mips/kernel/setup.c
@@ -667,7 +667,6 @@ static void __init arch_mem_init(char **cmdline_p)
mips_reserve_vmcore();
mips_parse_crashkernel();
- device_tree_init();
/*
* In order to reduce the possibility of kernel panic when failed to
@@ -798,6 +797,8 @@ void __init setup_arch(char **cmdline_p)
cpu_cache_init();
paging_init();
+ device_tree_init();
+
memblock_dump_all();
setup_rng_seed();
diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c
index da122a5fa43b..6f1a4232b8f0 100644
--- a/arch/nios2/kernel/setup.c
+++ b/arch/nios2/kernel/setup.c
@@ -169,8 +169,6 @@ void __init setup_arch(char **cmdline_p)
early_init_fdt_reserve_self();
early_init_fdt_scan_reserved_mem();
- unflatten_and_copy_device_tree();
-
setup_cpuinfo();
copy_exception_handler(cpuinfo.exception_addr);
@@ -189,4 +187,6 @@ void __init setup_arch(char **cmdline_p)
* get kmalloc into gear
*/
paging_init();
+
+ unflatten_and_copy_device_tree();
}
diff --git a/arch/openrisc/kernel/setup.c b/arch/openrisc/kernel/setup.c
index 9cf7fb60441f..fcda33bdca19 100644
--- a/arch/openrisc/kernel/setup.c
+++ b/arch/openrisc/kernel/setup.c
@@ -255,8 +255,6 @@ void calibrate_delay(void)
void __init setup_arch(char **cmdline_p)
{
- unflatten_and_copy_device_tree();
-
setup_cpuinfo();
#ifdef CONFIG_SMP
@@ -284,6 +282,8 @@ void __init setup_arch(char **cmdline_p)
/* paging_init() sets up the MMU and marks all pages as reserved */
paging_init();
+ unflatten_and_copy_device_tree();
+
*cmdline_p = boot_command_line;
printk(KERN_INFO "OpenRISC Linux -- http://openrisc.io\n");
diff --git a/arch/powerpc/kernel/setup-common.c b/arch/powerpc/kernel/setup-common.c
index 9b142b9d5187..58da58d02652 100644
--- a/arch/powerpc/kernel/setup-common.c
+++ b/arch/powerpc/kernel/setup-common.c
@@ -986,6 +986,9 @@ void __init setup_arch(char **cmdline_p)
paging_init();
+ /* Unflatten the device-tree passed by prom_init or kexec */
+ unflatten_device_tree();
+
/* Initialize the MMU context management stuff. */
mmu_context_init();
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index 3d80515298d2..2553696af21b 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -322,6 +322,8 @@ void __init setup_arch(char **cmdline_p)
/* Let earlyprintk output early console messages */
sh_early_platform_driver_probe("earlyprintk", 1, 1);
+ paging_init();
+
#ifdef CONFIG_OF_EARLY_FLATTREE
#ifdef CONFIG_USE_BUILTIN_DTB
unflatten_and_copy_device_tree();
@@ -329,9 +331,6 @@ void __init setup_arch(char **cmdline_p)
unflatten_device_tree();
#endif
#endif
-
- paging_init();
-
/* Perform the machine specific initialisation */
if (likely(sh_mv.mv_setup))
sh_mv.mv_setup(cmdline_p);
diff --git a/arch/um/kernel/dtb.c b/arch/um/kernel/dtb.c
index 484141b06938..04b0ada3b929 100644
--- a/arch/um/kernel/dtb.c
+++ b/arch/um/kernel/dtb.c
@@ -26,7 +26,6 @@ void uml_dtb_init(void)
}
early_init_fdt_scan_reserved_mem();
- unflatten_device_tree();
}
static int __init uml_dtb_setup(char *line, int *add)
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c
index b1bfed0c8528..fe6ecaa12ef2 100644
--- a/arch/um/kernel/um_arch.c
+++ b/arch/um/kernel/um_arch.c
@@ -7,6 +7,7 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/mm.h>
+#include <linux/of_fdt.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/panic_notifier.h>
@@ -421,6 +422,7 @@ void __init setup_arch(char **cmdline_p)
read_initrd();
paging_init();
+ unflatten_device_tree();
strscpy(boot_command_line, command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
setup_hostinfo(host_info, sizeof host_info);
diff --git a/arch/xtensa/kernel/setup.c b/arch/xtensa/kernel/setup.c
index bdec4a773af0..d20c56b4182e 100644
--- a/arch/xtensa/kernel/setup.c
+++ b/arch/xtensa/kernel/setup.c
@@ -355,13 +355,15 @@ void __init setup_arch(char **cmdline_p)
parse_early_param();
bootmem_init();
kasan_init();
- unflatten_and_copy_device_tree();
#ifdef CONFIG_SMP
smp_init_cpus();
#endif
paging_init();
+
+ unflatten_and_copy_device_tree();
+
zones_init();
#ifdef CONFIG_VT
--
2.17.1
On 12/3/2023 8:13 PM, Oreoluwa Babatunde wrote:
> The reserved_mem array is used to store the data of the different
> reserved memory regions specified in the DT of a device.
> The array stores information such as the name, node, starting address,
> and size of a reserved memory region.
>
> The array is currently statically allocated with a size of
> MAX_RESERVED_REGIONS(64). This means that any system that specifies a
> number of reserved memory regions greater than MAX_RESERVED_REGIONS(64)
> will not have enough space to store the information for all the regions.
>
> Therefore, this series extends the use of a static array for
> reserved_mem, and introduces a dynamically allocated array using
> memblock_alloc() based on the number of reserved memory regions
> specified in the DT.
>
> Memory gotten from memblock_alloc() is only writable after paging_init()
> is called, but the reserved memory regions need to be reserved before
> then so that the system does not create page table mappings for them.
>
> Reserved memory regions can be divided into 2 groups.
> i) Statically-placed reserved memory regions
> i.e. regions defined in the DT using the @reg property.
> ii) Dynamically-placed reserved memory regions.
> i.e. regions specified in the DT using the @alloc_ranges
> and @size properties.
>
> It is possible to call memblock_reserve() and memblock_mark_nomap() on
> the statically-placed reserved memory regions and not need to save them
> to the array until after paging_init(), but this is not possible for the
> dynamically-placed reserved memory because the starting address of these
> regions need to be stored somewhere after they are allocated.
>
> Therefore, this series achieves the allocation and population of the
> reserved_mem array in two steps:
>
> 1. Before paging_init()
> Before paging_init() is called, iterate through the reserved_mem
> nodes in the DT and do the following:
> - Allocate memory for dynamically-placed reserved memory regions and
> store their starting address in the static allocated reserved_mem
> array.
> - Call memblock_reserve() and memblock_mark_nomap() on all the
> reserved memory regions as needed.
> - Count the total number of reserved_mem nodes in the DT.
>
> 2. After paging_init()
> After paging_init() is called:
> - Allocate new memory for the reserved_mem array based on the number
> of reserved memory nodes in the DT.
> - Transfer all the information that was stored in the static array
> into the new array.
> - Store the rest of the reserved_mem regions in the new array.
> i.e. the statically-placed regions.
>
> The static array is no longer needed after this point, but there is
> currently no obvious way to free the memory. Therefore, the size of the
> initial static array is now defined using a config option.
> Because the array is used only before paging_init() to store the
> dynamically-placed reserved memory regions, the required size can vary
> from device to device. Therefore, scaling it can help get some memory
> savings.
>
> A possible solution to freeing the memory for the static array will be
> to mark it as __initdata. This will automatically free the memory once
> the init process is done running.
> The reason why this is not pursued in this series is because of
> the possibility of a use-after-free.
> If the dynamic allocation of the reserved_mem array fails, then future
> accesses of the reserved_mem array will still be referencing the static
> array. When the init process ends and the memory is freed up, any
> further attempts to use the reserved_mem array will result in a
> use-after-free.
>
> Note:
>
> - The limitation to this approach is that there is still a limit of
> 64 for dynamically reserved memory regions.
> - Upon further review, the series might need to be split up/duplicated
> for other archs.
>
>
> Oreoluwa Babatunde (6):
> of: reserved_mem: Change the order that reserved_mem regions are
> stored
> of: reserved_mem: Swicth call to unflatten_device_tree() to after
> paging_init()
> of: resevred_mem: Delay allocation of memory for dynamic regions
> of: reserved_mem: Add code to use unflattened DT for reserved_mem
> nodes
> of: reserved_mem: Add code to dynamically allocate reserved_mem array
> of: reserved_mem: Make MAX_RESERVED_REGIONS a config option
>
> arch/loongarch/kernel/setup.c | 2 +-
> arch/mips/kernel/setup.c | 3 +-
> arch/nios2/kernel/setup.c | 4 +-
> arch/openrisc/kernel/setup.c | 4 +-
> arch/powerpc/kernel/setup-common.c | 3 +
> arch/sh/kernel/setup.c | 5 +-
> arch/um/kernel/dtb.c | 1 -
> arch/um/kernel/um_arch.c | 2 +
> arch/xtensa/kernel/setup.c | 4 +-
> drivers/of/Kconfig | 13 +++
> drivers/of/fdt.c | 39 +++++--
> drivers/of/of_private.h | 6 +-
> drivers/of/of_reserved_mem.c | 175 +++++++++++++++++++++--------
> include/linux/of_reserved_mem.h | 8 +-
> kernel/dma/coherent.c | 4 +-
> kernel/dma/contiguous.c | 8 +-
> kernel/dma/swiotlb.c | 10 +-
> 17 files changed, 205 insertions(+), 86 deletions(-)
re-sending this to include maintainers for the other
archs.
Regards,
Oreoluwa