From 155c4d69fb3ecdc1bf8e47b7601c8894cddce4d6 Mon Sep 17 00:00:00 2001 From: Jaewon Kim Date: Mon, 29 Jun 2020 01:54:25 +0900 Subject: [PATCH] ANDROID: memblock: detect hidden memory hole size Bootloader knows the actual memory size, but bootloader may reserve some memory for a specific purpose and pass the only remaining memory region to kernel. Even though kernel does not know what it is, we need to detect those regions to sum up all reserved memory. Let me call it memory hole. To expect the hole size, this patch assume two things. One is that each physical memory has 1GB aligned size and address. And the hole is less than 1GB. For the hole, let it be shown as unknown in memsize logic. This is an example. 0x0bf000000-0x0c0000000 0x01000000 ( 16384 KB ) nomap unusable unknown Bug: 340432773 Signed-off-by: Jaewon Kim Link: https://lore.kernel.org/linux-mm/20240521023957.2587005-3-jaewon31.kim@samsung.com/ Change-Id: Idfccd369a8cb869dec5a2927626db723c6923e54 --- drivers/of/fdt.c | 2 ++ include/linux/memblock.h | 2 ++ mm/memblock.c | 44 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index a62dba9f2310..bfd262371fff 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -503,6 +503,8 @@ void __init early_init_fdt_scan_reserved_mem(void) if (!initial_boot_params) return; + memblock_memsize_detect_hole(); + fdt_scan_reserved_mem(); fdt_reserve_elfcorehdr(); diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 4e3706832fcf..e2b3a75f8ac7 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -596,9 +596,11 @@ static inline void memtest_report_meminfo(struct seq_file *m) { } #ifdef CONFIG_MEMBLOCK_MEMSIZE extern void __init_memblock memblock_memsize_record(const char *name, phys_addr_t base, phys_addr_t size, bool nomap, bool reusable); +extern void __init memblock_memsize_detect_hole(void); #else static inline void __init_memblock memblock_memsize_record(const char *name, phys_addr_t base, phys_addr_t size, bool nomap, bool reusable) { } +static inline void __init memblock_memsize_detect_hole(void) { } #endif #endif /* _LINUX_MEMBLOCK_H */ diff --git a/mm/memblock.c b/mm/memblock.c index 4f27c3f5bd16..b7b00f205fa6 100644 --- a/mm/memblock.c +++ b/mm/memblock.c @@ -2093,6 +2093,50 @@ void __init_memblock memblock_memsize_record(const char *name, phys_addr_t base, memblock_dbg("%s %pa..%pa nomap:%d reusable:%d\n", __func__, &base, &end, nomap, reusable); } + +void __init memblock_memsize_detect_hole(void) +{ + phys_addr_t base, end; + phys_addr_t prev_end, hole_sz; + int idx; + struct memblock_region *rgn; + int memblock_cnt = (int)memblock.memory.cnt; + + /* assume that the hole size is less than 1 GB */ + for_each_memblock_type(idx, (&memblock.memory), rgn) { + prev_end = (idx == 0) ? round_down(rgn->base, SZ_1G) : end; + base = rgn->base; + end = rgn->base + rgn->size; + + /* only for the last region, check a hole after the region */ + if (idx + 1 == memblock_cnt) { + hole_sz = round_up(end, SZ_1G) - end; + if (hole_sz) + memblock_memsize_record(NULL, end, hole_sz, + true, false); + } + + /* for each region, check a hole prior to the region */ + hole_sz = base - prev_end; + if (!hole_sz) + continue; + if (hole_sz < SZ_1G) { + memblock_memsize_record(NULL, prev_end, hole_sz, true, + false); + } else { + phys_addr_t hole_sz1, hole_sz2; + + hole_sz1 = round_up(prev_end, SZ_1G) - prev_end; + if (hole_sz1) + memblock_memsize_record(NULL, prev_end, + hole_sz1, true, false); + hole_sz2 = base % SZ_1G; + if (hole_sz2) + memblock_memsize_record(NULL, base - hole_sz2, + hole_sz2, true, false); + } + } +} #endif /* MEMBLOCK_MEMSIZE */ static void __init free_memmap(unsigned long start_pfn, unsigned long end_pfn)