Commit e94eb7ea authored by Björn Töpel's avatar Björn Töpel Committed by Alexandre Ghiti
Browse files

riscv: Properly export reserved regions in /proc/iomem



The /proc/iomem represents the kernel's memory map. Regions marked
with "Reserved" tells the user that the range should not be tampered
with. Kexec-tools, when using the older kexec_load syscall relies on
the "Reserved" regions to build the memory segments, that will be the
target of the new kexec'd kernel.

The RISC-V port tries to expose all reserved regions to userland, but
some regions were not properly exposed: Regions that resided in both
the "regular" and reserved memory block, e.g. the EFI Memory Map. A
missing entry could result in reserved memory being overwritten.

It turns out, that arm64, and loongarch had a similar issue a while
back:

  commit d91680e6 ("arm64: Fix /proc/iomem for reserved but not memory regions")
  commit 50d7ba36 ("arm64: export memblock_reserve()d regions via /proc/iomem")

Similar to the other ports, resolve the issue by splitting the regions
in an arch initcall, since we need a working allocator.

Fixes: ffe0e526 ("RISC-V: Improve init_resources()")
Signed-off-by: default avatarBjörn Töpel <bjorn@rivosinc.com>
Reviewed-by: default avatarAlexandre Ghiti <alexghiti@rivosinc.com>
Link: https://lore.kernel.org/r/20250409182129.634415-1-bjorn@kernel.org


Signed-off-by: default avatarAlexandre Ghiti <alexghiti@rivosinc.com>
parent 44101605
Loading
Loading
Loading
Loading
+35 −1
Original line number Diff line number Diff line
@@ -66,6 +66,9 @@ static struct resource bss_res = { .name = "Kernel bss", };
static struct resource elfcorehdr_res = { .name = "ELF Core hdr", };
#endif

static int num_standard_resources;
static struct resource *standard_resources;

static int __init add_resource(struct resource *parent,
				struct resource *res)
{
@@ -139,7 +142,7 @@ static void __init init_resources(void)
	struct resource *res = NULL;
	struct resource *mem_res = NULL;
	size_t mem_res_sz = 0;
	int num_resources = 0, res_idx = 0;
	int num_resources = 0, res_idx = 0, non_resv_res = 0;
	int ret = 0;

	/* + 1 as memblock_alloc() might increase memblock.reserved.cnt */
@@ -193,6 +196,7 @@ static void __init init_resources(void)
	/* Add /memory regions to the resource tree */
	for_each_mem_region(region) {
		res = &mem_res[res_idx--];
		non_resv_res++;

		if (unlikely(memblock_is_nomap(region))) {
			res->name = "Reserved";
@@ -210,6 +214,9 @@ static void __init init_resources(void)
			goto error;
	}

	num_standard_resources = non_resv_res;
	standard_resources = &mem_res[res_idx + 1];

	/* Clean-up any unused pre-allocated resources */
	if (res_idx >= 0)
		memblock_free(mem_res, (res_idx + 1) * sizeof(*mem_res));
@@ -221,6 +228,33 @@ static void __init init_resources(void)
	memblock_free(mem_res, mem_res_sz);
}

static int __init reserve_memblock_reserved_regions(void)
{
	u64 i, j;

	for (i = 0; i < num_standard_resources; i++) {
		struct resource *mem = &standard_resources[i];
		phys_addr_t r_start, r_end, mem_size = resource_size(mem);

		if (!memblock_is_region_reserved(mem->start, mem_size))
			continue;

		for_each_reserved_mem_range(j, &r_start, &r_end) {
			resource_size_t start, end;

			start = max(PFN_PHYS(PFN_DOWN(r_start)), mem->start);
			end = min(PFN_PHYS(PFN_UP(r_end)) - 1, mem->end);

			if (start > mem->end || end < mem->start)
				continue;

			reserve_region_with_split(mem, start, end, "Reserved");
		}
	}

	return 0;
}
arch_initcall(reserve_memblock_reserved_regions);

static void __init parse_dtb(void)
{