arm64: mm: fix accidental linear mapping of no-map reserved memory

When reserved-memory regions with the "no-map" property are not
page-aligned, the kernel may accidentally map them into the linear
mapping, contradicting the no-map semantics.

The root cause is a mismatch between /proc/iomem's address boundaries
and the actual page table mapping boundaries:

1. /proc/iomem derives its ranges from memblock via
   memblock_region_reserved_base_pfn/memblock_region_reserved_end_pfn,
   which perform PFN rounding so the displayed boundaries are
   page-aligned. This gives the impression that the no-map region
   occupies whole pages.

2. However, memblock_mark_nomap() splits memblock.memory regions at
   exact byte boundaries (memblock_isolate_range preserves raw DT
   base/size with no alignment). When for_each_mem_range iterates the
   non-NOMAP regions adjacent to a no-map region, it returns start/end
   values that are NOT page-aligned — they are the precise byte
   boundaries from the memblock split.

3. These sub-page-aligned values are passed to
   __create_pgd_mapping_locked(), which does:
     phys &= PAGE_MASK;
     addr = virt & PAGE_MASK;
     end = PAGE_ALIGN(virt + size);
   The downward rounding of phys via PAGE_MASK extends the mapped
   range backward into the adjacent no-map region, effectively
   including no-map memory in the linear mapping.

For example, with 64K pages, reserved_region@A2000000 (base=0xA2000000,
size=0x8000, no-map) causes for_each_mem_range to return
start=0xA2008000 for the next mappable region. After phys &= PAGE_MASK,
the actual mapping starts at 0xA2000000 — the entire no-map region is
incorrectly mapped.

Fix this by rounding the mappable range inward to PAGE_SIZE boundaries
before passing it to __map_memblock: start is rounded UP and end is
rounded DOWN. This ensures the mapped area never overlaps with adjacent
no-map regions. The cost is at most one page of unmapped gap at each
boundary, which is preferable to violating no-map semantics.

Signed-off-by: liulhong617 <liulhong617@gmail.com>
Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
liulhong617 2026-05-13 09:02:55 +08:00 committed by Will Deacon
parent 3b56ebafec
commit 7ace06a01e

View File

@ -1190,6 +1190,20 @@ static void __init map_mem(void)
/* map all the memory banks */
for_each_mem_range(i, &start, &end) {
/*
* for_each_mem_range may return sub-page-aligned boundaries
* after memblock_mark_nomap() splits regions at byte precision.
* __create_pgd_mapping_locked aligns phys down to PAGE_MASK,
* which could accidentally map no-map memory on the boundary.
* Round the mappable range inward: start UP, end DOWN, so
* that the mapped area never overlaps with adjacent no-map
* regions. The cost is at most one page of unmapped gap at
* each boundary.
*/
start = PAGE_ALIGN(start);
end = end & PAGE_MASK;
if (start >= end)
continue;
/*
* The linear map must allow allocation tags reading/writing
* if MTE is present. Otherwise, it has the same attributes as