arm64: relax Image placement rules
Subsystems (3)
v3 cover arm64: relax Image placement rules
This series updates the mapping of the kernel Image and the linear mapping of system memory to allow more freedom in the choice of Image placement without affecting the accessibility of system RAM below the kernel Image, and the mapping efficiency (i.e., memory can always be mapped in 512 MB or 1 GB blocks).
An added benefit of having the freedom to put the Image anywhere in physical memory is that it decomplicates the logic that needs to be applied when using kexec to boot another kernel. Furthermore, moving the kernel out of the linear mapping is a preparatory step towards implementing kASLR. This will be addressed in a followup series.
Changes since v2 (http://marc.info/?l=linux-arm-kernel&m=144309211310754):
- rebased onto v4.4-rc1, which adds support for 16k pages, resulting in minor changes requried to various parts of the code
- fix behavior under CONFIG_DEBUG_RODATA
Changes since v1:
- dropped somewhat unrelated patch #1 and patches #2 and #3 that have been merged separately
- rebased onto v4.2-rc3
- tweak the generic early_init_dt_add_memory_arch for our purposes rather than clone the implementation completely
Known issues:
- the mem= command line parameter works correctly now, but removes memory from the bottom first before clipping from the top, which may be undesirable since it may discard precious memory below the 4 GB boundary.
Patch #1 refactors the generic early_init_dt_add_memory_arch implementation to allow to minimum memblock address to be overridden by the architecture.
Patch #2 changes the memblock_reserve logic so that unused page table reservations are left unreserved in memblock.
Patch #3 refactors early_fixmap_init() so that we can reuse its core for bootstrapping other memory mappings.
Patch #4 bootstraps the linear mapping explicitly. Up until now, this was done implicitly due to the fact that the linear mapping starts at the base of the kernel Image.
Patch #5 moves the mapping of the kernel Image outside of the linear mapping.
Patch #6 changes the attributes of the linear mapping to non-executable since we don't execute code from it anymore.
Patch #7 allows the kernel to be loaded at any 2 MB aligned offset in physical memory, by assigning PHYS_OFFSET based on the available memory and not based on the physical address of the base of the kernel Image.
Ard Biesheuvel (7):
of/fdt: make memblock minimum physical address arch configurable
arm64: use more granular reservations for static page table
allocations
arm64: split off early mapping code from early_fixmap_init()
arm64: mm: explicitly bootstrap the linear mapping
arm64: move kernel mapping out of linear region
arm64: map linear region as non-executable
arm64: allow kernel Image to be loaded anywhere in physical memory
Documentation/arm64/booting.txt | 12 +- arch/arm64/include/asm/boot.h | 7 + arch/arm64/include/asm/compiler.h | 2 + arch/arm64/include/asm/kernel-pgtable.h | 5 +- arch/arm64/include/asm/memory.h | 27 ++- arch/arm64/kernel/head.S | 18 +- arch/arm64/kernel/vmlinux.lds.S | 39 +++- arch/arm64/mm/dump.c | 3 +- arch/arm64/mm/init.c | 54 ++++- arch/arm64/mm/mmu.c | 224 +++++++++++++------- drivers/of/fdt.c | 5 +- 11 files changed, 284 insertions(+), 112 deletions(-)
Signature · 1 line
1.9.1
PATCH v3 1/7 of/fdt: make memblock minimum physical address arch configurable
By default, early_init_dt_add_memory_arch() ignores memory below the base of the kernel image since it won't be addressable via the linear mapping. However, this is not appropriate anymore once we decouple the kernel text mapping from the linear mapping, so archs may want to drop the low limit entirely. So allow the minimum to be overridden by setting MIN_MEMBLOCK_ADDR.
--- drivers/of/fdt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/of/fdt.c +4 -1
@@ -971,13 +971,16 @@ int __init early_init_dt_scan_chosen(unsigned long node, const char *uname, }#ifdef CONFIG_HAVE_MEMBLOCK +#ifndef MIN_MEMBLOCK_ADDR +#define MIN_MEMBLOCK_ADDR __pa(PAGE_OFFSET) +#endif #ifndef MAX_MEMBLOCK_ADDR #define MAX_MEMBLOCK_ADDR ((phys_addr_t)~0)#endif void __init __weak early_init_dt_add_memory_arch (u64 base,u64 size) { -const u64 phys_offset =__pa (PAGE_OFFSET ); +const u64 phys_offset =MIN_MEMBLOCK_ADDR ;if (!PAGE_ALIGNED (base)) {if (size <PAGE_SIZE - (base & ~PAGE_MASK )) {
Signature · 1 line
1.9.1
PATCH v3 2/7 arm64: use more granular reservations for static page table allocations
In reply to Ard BiesheuvelBefore introducing new statically allocated page tables and increasing their alignment in subsequent patches, update the reservation logic so that only pages that are in actual use end up as reserved with memblock.
--- arch/arm64/mm/init.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
arch/arm64/mm/init.c +4 -1
@@ -36,6 +36,7 @@#include <linux/swiotlb.h> #include <asm/fixmap.h> +#include <asm/kernel-pgtable.h> #include <asm/memory.h> #include <asm/sections.h> #include <asm/setup.h>
@@ -165,11 +166,13 @@ void __init arm64_memblock_init(void)* Register the kernel text, kernel data, initrd, and initial * pagetables with memblock. */ -memblock_reserve (__pa (_text), _end - _text); +memblock_reserve (__pa (_text), __bss_stop - _text);#ifdef CONFIG_BLK_DEV_INITRD if (initrd_start)memblock_reserve (__virt_to_phys (initrd_start), initrd_end - initrd_start);#endif +memblock_reserve (__pa (idmap_pg_dir),IDMAP_DIR_SIZE ); +memblock_reserve (__pa (swapper_pg_dir),SWAPPER_DIR_SIZE );early_init_fdt_scan_reserved_mem ();
Signature · 1 line
1.9.1
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
In reply to Ard BiesheuvelThis splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
--- arch/arm64/include/asm/compiler.h | 2 + arch/arm64/kernel/vmlinux.lds.S | 12 ++-- arch/arm64/mm/mmu.c | 60 ++++++++++++++------ 3 files changed, 51 insertions(+), 23 deletions(-)
arch/arm64/include/asm/compiler.h +2 -0
@@ -27,4 +27,6 @@*/ #define __asmeq (x, y) ".ifnc " x "," y " ; .err ; .endif\n\t" +#define __pgdir __attribute__((section(".pgdir"),aligned(PAGE_SIZE))) +#endif /* __ASM_COMPILER_H */
arch/arm64/kernel/vmlinux.lds.S +7 -5
@@ -164,11 +164,13 @@ SECTIONS BSS_SECTION(0, 0, 0) - . = ALIGN(PAGE_SIZE); - idmap_pg_dir = .; - . += IDMAP_DIR_SIZE; - swapper_pg_dir = .; - . += SWAPPER_DIR_SIZE; + .pgdir (NOLOAD) : ALIGN(PAGE_SIZE) { + idmap_pg_dir = .; + . += IDMAP_DIR_SIZE; + swapper_pg_dir = .; + . += SWAPPER_DIR_SIZE; + *(.pgdir) + } _end = .;
arch/arm64/mm/mmu.c +42 -18
83 lines not on this page · raw message
Signature · 1 line
1.9.1
Mark Rutland
reviewerPATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Hi Ard,
Apologies that it's taken me so long to get around to this...
Quoted 8 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
By adding new FIX_{PGD,PUD,PMD,PTE} indicees to the fixmap, we can allocate page tables from anywhere via memblock, and temporarily map them as we need to.
That would avoid the need for the bootstrap tables. In head.S we'd only need to create a temporary (coarse-grained, RWX) kernel mapping (with the fixmap bolted on). Later we would create a whole new set of tables with a fine-grained kernel mapping and a full linear mapping using the new fixmap entries to temporarily map tables, then switch over to those atomically.
Otherwise, one minor comment below.
arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 407
Quoted 3 lines
arch/arm64/mm/mmu.c +3
-0+static void __init bootstrap_early_mapping (unsigned long addr, +struct bootstrap_pgtables *reg, +bool pte_level)
The only caller in this patch passes true for pte_level.
Can we not introduce the argument when it is first needed? Or at least have something in the commit message as to why we'll need it later?
arch/arm64/mm/mmu.c static inline pte_t * fixmap_pte(unsigned long addr) · line 677
Quoted 5 lines
arch/arm64/mm/mmu.c +1
-1/* * The boot-ioremap range spans multiple pmds, for which -* we are not preparted: +* we are not prepared: */
I cannot wait to see this typo go!
Otherwise, this looks fine to me.
Thanks,
Mark.
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Quoted 14 lines · Mark Rutland
Hi Ard,
Apologies that it's taken me so long to get around to this...
Quoted 8 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
Quoted 8 lines
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
By adding new FIX_{PGD,PUD,PMD,PTE} indicees to the fixmap, we can allocate page tables from anywhere via memblock, and temporarily map them as we need to.
Interesting. So how are you dealing with the va<->pa translations and vice versa that occur all over the place in create_mapping() et al ?
Quoted 7 lines
That would avoid the need for the bootstrap tables. In
head.Swe'd only need to create a temporary (coarse-grained, RWX) kernel mapping (with the fixmap bolted on). Later we would create a whole new set of tables with a fine-grained kernel mapping and a full linear mapping using the new fixmap entries to temporarily map tables, then switch over to those atomically.
If we change back to a full linear mapping, are we back to not putting the Image astride a 1GB/32MB/512MB boundary (depending on page size)?
Anyway, to illustrate where I am headed with this: in my next version of this series, I intend to move the kernel mapping to the start of the vmalloc area, which gets moved up 64 MB to make room for the module area (which also moves down). That way, we can still load modules as before, but no longer have a need for a dedicated carveout for the kernel below PAGE_OFFSET.
The next step is then to move the kernel Image up inside the vmalloc area based on some randomness we get from the bootloader, and relocate it in place (using the same approach as in the patches I sent out beginning of this year). I have implemented module PLTs so that the Image and the modules no longer need to be within 128 MB of each other, which means that we can have full KASLR for modules and Image, and also place the kernel anywhere in physical memory.The module PLTs would be a runtime penalty only, i.e., a KASLR capable kernel running without KASLR would not incur the penalty of branching via PLTs. The only build time option is -mcmodel=large for modules so that data symbol references are absolute, but that is unlike to hurt performance.
Quoted 11 lines
Otherwise, one minor comment below.
arch/arm64/mm/mmu.c static void __init __map_memblock(phys_addr_t start, phys_addr_t end) · line 407
Quoted 3 lines
arch/arm64/mm/mmu.c +3
-0+static void __init bootstrap_early_mapping (unsigned long addr, +struct bootstrap_pgtables *reg, +bool pte_level)The only caller in this patch passes true for
pte_level.Can we not introduce the argument when it is first needed? Or at least have something in the commit message as to why we'll need it later?
Yes, that should be possible.
Quoted 10 lines
arch/arm64/mm/mmu.c static inline pte_t * fixmap_pte(unsigned long addr) · line 677
Quoted 5 lines
/*
* The boot-ioremap range spans multiple pmds, for whicharch/arm64/mm/mmu.c +1
-1-* we are not preparted: +* we are not prepared: */ I cannot wait to see this typo go!
Otherwise, this looks fine to me.
Thanks Mark
Mark Rutland
reviewerPATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Quoted 19 lines · Ard Biesheuvel
Quoted 18 lines · Mark Rutland
Hi Ard,
Apologies that it's taken me so long to get around to this...
Quoted 8 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
Quoted 7 lines
Quoted 4 lines
By adding new FIX_{PGD,PUD,PMD,PTE} indicees to the fixmap, we can allocate page tables from anywhere via memblock, and temporarily map them as we need to.
Interesting. So how are you dealing with the va<->pa translations and vice versa that occur all over the place in
create_mapping()et al ?
By rewriting create_mapping() et al to not do that ;)
That's requiring a fair amount of massaging, but so far I've not hit anything that renders the approach impossible.
Quoted 10 lines
Quoted 7 lines
That would avoid the need for the bootstrap tables. In
head.Swe'd only need to create a temporary (coarse-grained, RWX) kernel mapping (with the fixmap bolted on). Later we would create a whole new set of tables with a fine-grained kernel mapping and a full linear mapping using the new fixmap entries to temporarily map tables, then switch over to those atomically.If we change back to a full linear mapping, are we back to not putting the Image astride a 1GB/32MB/512MB boundary (depending on page size)?
I'm not exactly sure what you mean here.
The kernel mapping may inhibit using large section mappings, but this is necessary anyway due to permission changes at sub-section granularity (e.g. in fixup_init).
The idea is that when the kernel tables are set up, things are mapped at the largest possible granularity that permits later permission changes without breaking/making sections (such that we can avoid TLB conflicts).
So we'd map the kernel and memory in segments, where no two segments share a common last-level entry (i.e. they're all at least page-aligned, and don't share a section with another segment).
We'd have separate segments for:
* memory below TEXT_OFFSET
* text
* rodata
* init
* altinstr (I think this can be folded into rodata)
* bss / data, tables
* memory above _end
Later I think it should be relatively simple to move the memory segment mapping for split-VA.
Quoted 6 lines
Anyway, to illustrate where I am headed with this: in my next version of this series, I intend to move the kernel mapping to the start of the vmalloc area, which gets moved up 64 MB to make room for the module area (which also moves down). That way, we can still load modules as before, but no longer have a need for a dedicated carveout for the kernel below
PAGE_OFFSET.
Ok.
Quoted 4 lines
The next step is then to move the kernel Image up inside the vmalloc area based on some randomness we get from the bootloader, and relocate it in place (using the same approach as in the patches I sent out beginning of this year). I have implemented module PLTs so that the
Quoted 8 lines
Image and the modules no longer need to be within 128 MB of each other, which means that we can have full KASLR for modules and Image, and also place the kernel anywhere in physical memory.The module PLTs would be a runtime penalty only, i.e., a KASLR capable kernel running without KASLR would not incur the penalty of branching via PLTs. The only build time option is -mcmodel=large for modules so that data symbol references are absolute, but that is unlike to hurt performance.
I'm certainly interested in seeing this!
Thanks,
Mark.
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Quoted 41 lines · Mark Rutland
Quoted 19 lines · Ard Biesheuvel
Quoted 18 lines · Mark Rutland
Hi Ard,
Apologies that it's taken me so long to get around to this...
Quoted 8 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
Quoted 7 lines
Quoted 4 lines
By adding new FIX_{PGD,PUD,PMD,PTE} indicees to the fixmap, we can allocate page tables from anywhere via memblock, and temporarily map them as we need to.
Interesting. So how are you dealing with the va<->pa translations and vice versa that occur all over the place in
create_mapping()et al ?By rewriting
create_mapping()et al to not do that ;)That's requiring a fair amount of massaging, but so far I've not hit anything that renders the approach impossible.
Quoted 8 lines
Quoted 7 lines
That would avoid the need for the bootstrap tables. In
head.Swe'd only need to create a temporary (coarse-grained, RWX) kernel mapping (with the fixmap bolted on). Later we would create a whole new set of tables with a fine-grained kernel mapping and a full linear mapping using the new fixmap entries to temporarily map tables, then switch over to those atomically.
Quoted 5 lines
Quoted 2 lines
If we change back to a full linear mapping, are we back to not putting the Image astride a 1GB/32MB/512MB boundary (depending on page size)?
I'm not exactly sure what you mean here.
Apologies, I misread 'linear mapping' as 'id mapping, which of course are two different things entirely
Quoted 21 lines
The kernel mapping may inhibit using large section mappings, but this is necessary anyway due to permission changes at sub-section granularity (e.g. in
fixup_init).The idea is that when the kernel tables are set up, things are mapped at the largest possible granularity that permits later permission changes without breaking/making sections (such that we can avoid TLB conflicts).
So we'd map the kernel and memory in segments, where no two segments share a common last-level entry (i.e. they're all at least page-aligned, and don't share a section with another segment).
We'd have separate segments for:
* memory belowTEXT_OFFSET
* text
* rodata
* init
* altinstr (I think this can be folded into rodata)
* bss / data, tables
* memory above_end
Quoted 3 lines
Later I think it should be relatively simple to move the memory segment mapping for split-VA.
I'd need to see it to understand, I guess, but getting rid of the pa<->va translations is definitely an improvement for the stuff I am trying to do, and would probably make it a lot cleaner.
Quoted 22 lines
Quoted 6 lines
Anyway, to illustrate where I am headed with this: in my next version of this series, I intend to move the kernel mapping to the start of the vmalloc area, which gets moved up 64 MB to make room for the module area (which also moves down). That way, we can still load modules as before, but no longer have a need for a dedicated carveout for the kernel below
PAGE_OFFSET.Ok.
Quoted 4 lines
The next step is then to move the kernel Image up inside the vmalloc area based on some randomness we get from the bootloader, and relocate it in place (using the same approach as in the patches I sent out beginning of this year). I have implemented module PLTs so that the
Quoted 8 lines
Image and the modules no longer need to be within 128 MB of each other, which means that we can have full KASLR for modules and Image, and also place the kernel anywhere in physical memory.The module PLTs would be a runtime penalty only, i.e., a KASLR capable kernel running without KASLR would not incur the penalty of branching via PLTs. The only build time option is -mcmodel=large for modules so that data symbol references are absolute, but that is unlike to hurt performance.
Quoted 2 lines
I'm certainly interested in seeing this!
I have patches for all of this, only they don't live on the same branch yet :-)
Catalin Marinas
maintainerPATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
In reply to Ard BiesheuvelQuoted 8 lines · Ard Biesheuvel
Quoted 7 lines · Mark Rutland
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
By adding new FIX_{PGD,PUD,PMD,PTE} indicees to the fixmap, we can allocate page tables from anywhere via memblock, and temporarily map them as we need to.
[...]
Quoted 6 lines
Quoted 6 lines
That would avoid the need for the bootstrap tables. In
head.Swe'd only need to create a temporary (coarse-grained, RWX) kernel mapping (with the fixmap bolted on). Later we would create a whole new set of tables with a fine-grained kernel mapping and a full linear mapping using the new fixmap entries to temporarily map tables, then switch over to those atomically.
If we separate the kernel image mapping from the linear one, I think things would be slightly simpler to avoid TLB conflicts (but I haven't looked at Mark's patches yet).
Quoted 9 lines
If we change back to a full linear mapping, are we back to not putting the Image astride a 1GB/32MB/512MB boundary (depending on page size)?
Anyway, to illustrate where I am headed with this: in my next version of this series, I intend to move the kernel mapping to the start of the vmalloc area, which gets moved up 64 MB to make room for the module area (which also moves down). That way, we can still load modules as before, but no longer have a need for a dedicated carveout for the kernel below
PAGE_OFFSET.
This makes sense, I guess it can be easily added to the existing series just by changing the KIMAGE_OFFSET macro.
Quoted 4 lines
The next step is then to move the kernel Image up inside the vmalloc area based on some randomness we get from the bootloader, and relocate it in place (using the same approach as in the patches I sent out beginning of this year). I have implemented module PLTs so that the
Quoted 8 lines
Image and the modules no longer need to be within 128 MB of each other, which means that we can have full KASLR for modules and Image, and also place the kernel anywhere in physical memory.The module PLTs would be a runtime penalty only, i.e., a KASLR capable kernel running without KASLR would not incur the penalty of branching via PLTs. The only build time option is -mcmodel=large for modules so that data symbol references are absolute, but that is unlike to hurt performance.
I guess full KASLR would be conditional on a config option.
Signature · 1 line
Catalin
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Quoted 31 lines · Catalin Marinas
Quoted 8 lines · Ard Biesheuvel
Quoted 7 lines · Mark Rutland
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
By adding new FIX_{PGD,PUD,PMD,PTE} indicees to the fixmap, we can allocate page tables from anywhere via memblock, and temporarily map them as we need to.
[...]
Quoted 6 lines
Quoted 6 lines
That would avoid the need for the bootstrap tables. In
head.Swe'd only need to create a temporary (coarse-grained, RWX) kernel mapping (with the fixmap bolted on). Later we would create a whole new set of tables with a fine-grained kernel mapping and a full linear mapping using the new fixmap entries to temporarily map tables, then switch over to those atomically.If we separate the kernel image mapping from the linear one, I think things would be slightly simpler to avoid TLB conflicts (but I haven't looked at Mark's patches yet).
Quoted 9 lines
If we change back to a full linear mapping, are we back to not putting the Image astride a 1GB/32MB/512MB boundary (depending on page size)?
Anyway, to illustrate where I am headed with this: in my next version of this series, I intend to move the kernel mapping to the start of the vmalloc area, which gets moved up 64 MB to make room for the module area (which also moves down). That way, we can still load modules as before, but no longer have a need for a dedicated carveout for the kernel below
PAGE_OFFSET.
Quoted 3 lines
This makes sense, I guess it can be easily added to the existing series just by changing the
KIMAGE_OFFSETmacro.
Indeed. The only difference is that the VM area needs to be reserved explicitly, to prevent vmalloc() from reusing it.
Quoted 13 lines
Quoted 4 lines
The next step is then to move the kernel Image up inside the vmalloc area based on some randomness we get from the bootloader, and relocate it in place (using the same approach as in the patches I sent out beginning of this year). I have implemented module PLTs so that the
Quoted 8 lines
Image and the modules no longer need to be within 128 MB of each other, which means that we can have full KASLR for modules and Image, and also place the kernel anywhere in physical memory.The module PLTs would be a runtime penalty only, i.e., a KASLR capable kernel running without KASLR would not incur the penalty of branching via PLTs. The only build time option is -mcmodel=large for modules so that data symbol references are absolute, but that is unlike to hurt performance.
Quoted 2 lines
I guess full KASLR would be conditional on a config option.
Yes. But it would be nice if the only build time penalty is the use of -mcmodel=large for modules, so that distro kernels can enable KASLR unconditionally (especially since -mcmodel=large is likely to be enabled for distro kernels anyway, due to the A53 erratum that requires it.)
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
In reply to Mark RutlandQuoted 7 lines · Mark Rutland
Apologies that it's taken me so long to get around to this...
Quoted 4 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
Quoted 8 lines
Quoted 4 lines
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
Given that the Christmas break is around the corner and your TLB series is probably going to take some time to get right, I suggest we persevere with Ard's current patch series for 4.5 and merge the TLB conflict solution for 4.6. I don't want us to end up in a situation where this is needlessly blocked on something that isn't quite ready.
Any objections? If not, Ard -- can you post a new version of this, please?
Will
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Quoted 17 lines · Will Deacon
Quoted 7 lines · Mark Rutland
Apologies that it's taken me so long to get around to this...
Quoted 4 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
Quoted 8 lines
Quoted 4 lines
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
Quoted 8 lines
Given that the Christmas break is around the corner and your TLB series is probably going to take some time to get right, I suggest we persevere with Ard's current patch series for 4.5 and merge the TLB conflict solution for 4.6. I don't want us to end up in a situation where this is needlessly blocked on something that isn't quite ready.
Any objections? If not, Ard -- can you post a new version of this, please?
Happy to post a new version, with the following remarks
- my current private tree has evolved in the mean time, and I am now putting the kernel image at the base of the vmalloc region (and the module region right before)
- I think Mark's changes would allow me to deobfuscate the VA bias that redirects __va() translations into the kernel VA space rather than the linear mapping
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Quoted 27 lines · Ard Biesheuvel
Quoted 17 lines · Will Deacon
Quoted 7 lines · Mark Rutland
Apologies that it's taken me so long to get around to this...
Quoted 4 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
Quoted 8 lines
Quoted 4 lines
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
Quoted 8 lines
Given that the Christmas break is around the corner and your TLB series is probably going to take some time to get right, I suggest we persevere with Ard's current patch series for 4.5 and merge the TLB conflict solution for 4.6. I don't want us to end up in a situation where this is needlessly blocked on something that isn't quite ready.
Any objections? If not, Ard -- can you post a new version of this, please?
Quoted 7 lines
Happy to post a new version, with the following remarks
- my current private tree has evolved in the mean time, and I am now putting the kernel image at the base of the vmalloc region (and the module region right before)
- I think Mark's changes would allow me to deobfuscate the VA bias that redirects__va()translations into the kernel VA space rather than the linear mapping
I'll leave that up to you. I'm just trying to avoid you growing a dependency on something that's unlikely to make it for 4.5. If Mark separates out the parts you need, perhaps that offers us some middle ground.
Will
PATCH v3 3/7 arm64: split off early mapping code from early_fixmap_init()
Quoted 36 lines · Will Deacon
Quoted 27 lines · Ard Biesheuvel
Quoted 17 lines · Will Deacon
Quoted 7 lines · Mark Rutland
Apologies that it's taken me so long to get around to this...
Quoted 4 lines · Ard Biesheuvel
This splits off and generalises the population of the statically allocated fixmap page tables so that we may reuse it later for the linear mapping once we move the kernel text mapping out of it.
Quoted 8 lines
Quoted 4 lines
This also involves taking into account that table entries at any of the levels we are populating may have been populated already, since the fixmap mapping might not be disjoint up to the pgd level anymore from other early mappings.
As a heads-up, for avoiding TLB conflicts, I'm currently working on alternative way of creating the kernel page tables which will definitely conflict here, and may or may not supercede this approach.
Quoted 8 lines
Given that the Christmas break is around the corner and your TLB series is probably going to take some time to get right, I suggest we persevere with Ard's current patch series for 4.5 and merge the TLB conflict solution for 4.6. I don't want us to end up in a situation where this is needlessly blocked on something that isn't quite ready.
Any objections? If not, Ard -- can you post a new version of this, please?
Quoted 7 lines
Happy to post a new version, with the following remarks
- my current private tree has evolved in the mean time, and I am now putting the kernel image at the base of the vmalloc region (and the module region right before)
- I think Mark's changes would allow me to deobfuscate the VA bias that redirects__va()translations into the kernel VA space rather than the linear mapping
Quoted 4 lines
I'll leave that up to you. I'm just trying to avoid you growing a dependency on something that's unlikely to make it for 4.5. If Mark separates out the parts you need, perhaps that offers us some middle ground.
I have played around with Mark's code a bit, and it looks like it's a huge improvement for the split VA patches as well: I have a patch that removes early_fixmap_init's dependency on the linear mapping, and combined with Mark's patches to use the fixmap for manipulating the page tables, it looks like I no longer need the VA bias to redirect __va translations into the kernel mapping early on.
PATCH v3 4/7 arm64: mm: explicitly bootstrap the linear mapping
In reply to Ard BiesheuvelIn preparation of moving the kernel text out of the linear mapping, ensure that the part of the kernel Image that contains the statically allocated page tables is made accessible via the linear mapping before performing the actual mapping of all of memory. This is needed by the normal mapping routines, that rely on the linear mapping to walk the page tables while manipulating them.
In addition, explicitly map the start of DRAM and set the memblock limit so that all early memblock allocations are done from a region that is guaranteed to be mapped.
--- arch/arm64/kernel/vmlinux.lds.S | 18 +++- arch/arm64/mm/mmu.c | 93 +++++++++++++++----- 2 files changed, 86 insertions(+), 25 deletions(-)
arch/arm64/kernel/vmlinux.lds.S +17 -1
@@ -72,6 +72,17 @@ PECOFF_FILE_ALIGNMENT = 0x200; #define ALIGN_DEBUG_RO_MIN(min) . = ALIGN(min); #endif +/* + * The pgdir region needs to be mappable using a single PMD or PUD sized region, + * so align it to a power-of-2 upper bound of its size. 16k/4 levels needs 20 + * pages at the most, every other config needs at most 16 pages. + */ +#if defined(CONFIG_ARM64_16K_PAGES) && CONFIG_ARM64_PGTABLE_LEVELS == 4 +#define PGDIR_ALIGN (32 * PAGE_SIZE) +#else +#define PGDIR_ALIGN (16 * PAGE_SIZE) +#endif + SECTIONS { /*
@@ -164,7 +175,7 @@ SECTIONS BSS_SECTION(0, 0, 0) - .pgdir (NOLOAD) : ALIGN(PAGE_SIZE) { + .pgdir (NOLOAD) : ALIGN(PGDIR_ALIGN) { idmap_pg_dir = .; . += IDMAP_DIR_SIZE; swapper_pg_dir = .;
@@ -189,6 +200,11 @@ ASSERT(__idmap_text_end - (__idmap_text_start & ~(SZ_4K - 1)) <= SZ_4K, "ID map text too big or misaligned") /* + * Check that the chosen PGDIR_ALIGN value is sufficient. + */ +ASSERT(SIZEOF(.pgdir) <= ALIGNOF(.pgdir), ".pgdir size exceeds its alignment") + +/* * If padding is applied before .head.text, virt<->phys conversions will fail. */ ASSERT(_text == (PAGE_OFFSET + TEXT_OFFSET), "HEAD is misaligned")
arch/arm64/mm/mmu.c +69 -24
116 lines not on this page · raw message
Signature · 1 line
1.9.1
PATCH v3 5/7 arm64: move kernel mapping out of linear region
In reply to Ard BiesheuvelThis moves the primary mapping of the kernel Image out of the linear region. This is a preparatory step towards allowing the kernel Image to reside anywhere in physical memory without affecting the ability to map all of it efficiently.
--- arch/arm64/include/asm/boot.h | 7 +++++++ arch/arm64/include/asm/kernel-pgtable.h | 5 +++-- arch/arm64/include/asm/memory.h | 20 +++++++++++++++++--- arch/arm64/kernel/head.S | 18 +++++++++++++----- arch/arm64/kernel/vmlinux.lds.S | 11 +++++++++-- arch/arm64/mm/dump.c | 3 ++- arch/arm64/mm/mmu.c | 19 ++++++++++++++----- 7 files changed, 65 insertions(+), 18 deletions(-)
arch/arm64/include/asm/boot.h +7 -0
@@ -11,4 +11,11 @@#define MIN_FDT_ALIGN 8#define MAX_FDT_SIZE SZ_2M +/* +* arm64 requires the kernel image to be 2 MB aligned and +* not exceed 64 MB in size. +*/ +#define MIN_KIMG_ALIGN SZ_2M +#define MAX_KIMG_SIZE SZ_64M +#endif
arch/arm64/include/asm/kernel-pgtable.h +3 -2
@@ -70,8 +70,9 @@/* * Initial memory map attributes. */ -#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED) -#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) +#define SWAPPER_PTE_FLAGS (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED | PTE_UXN) +#define SWAPPER_PMD_FLAGS (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S | \ + PMD_SECT_UXN)#if ARM64_SWAPPER_USES_SECTION_MAPS #define SWAPPER_MM_MMUFLAGS (PMD_ATTRINDX(MT_NORMAL) | SWAPPER_PMD_FLAGS)
arch/arm64/include/asm/memory.h +17 -3
@@ -24,6 +24,7 @@#include <linux/compiler.h> #include <linux/const.h> #include <linux/types.h> +#include <asm/boot.h> #include <asm/sizes.h> /*
@@ -39,7 +40,12 @@#define PCI_IO_SIZE SZ_16M/* -* PAGE_OFFSET - the virtual address of the start of the kernel image (top +* Offset below PAGE_OFFSET where to map the kernel Image. +*/ +#define KIMAGE_OFFSET MAX_KIMG_SIZE + +/* +* PAGE_OFFSET - the virtual address of the base of the linear mapping (top * (VA_BITS - 1)) * VA_BITS - the maximum number of bits for virtual addresses. * VA_START - the first kernel virtual address.
@@ -51,7 +57,8 @@#define VA_BITS (CONFIG_ARM64_VA_BITS)#define VA_START (UL(0xffffffffffffffff) << VA_BITS)#define PAGE_OFFSET (UL(0xffffffffffffffff) << (VA_BITS - 1)) -#define MODULES_END (PAGE_OFFSET) +#define KIMAGE_VADDR (PAGE_OFFSET - KIMAGE_OFFSET) +#define MODULES_END KIMAGE_VADDR#define MODULES_VADDR (MODULES_END - SZ_64M)#define PCI_IO_END (MODULES_VADDR - SZ_2M)#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE)
@@ -75,7 +82,11 @@* private definitions which should NOT be used outside memory.h * files. Use virt_to_phys/phys_to_virt/__pa/__va instead. */ -#define __virt_to_phys (x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET)) +#define __virt_to_phys (x) ({ \ + long __x = (long)(x) - PAGE_OFFSET; \ + __x >= 0 ? (phys_addr_t)(__x + PHYS_OFFSET) : \ + (phys_addr_t)(__x + PHYS_OFFSET + kernel_va_offset); }) +#define __phys_to_virt (x) ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))/*
@@ -106,6 +117,8 @@ extern phys_addr_t memstart_addr;/* PHYS_OFFSET - the physical address of the start of memory. */ #define PHYS_OFFSET ({ memstart_addr; }) +extern u64 kernel_va_offset; +/* * The maximum physical address that the linear direct mapping * of system RAM can cover. (PAGE_OFFSET can be interpreted as
@@ -113,6 +126,7 @@ extern phys_addr_t memstart_addr;* maximum size of the linear mapping.) */ #define MAX_MEMBLOCK_ADDR ({ memstart_addr - PAGE_OFFSET - 1; }) +#define MIN_MEMBLOCK_ADDR __pa(KIMAGE_VADDR)/* * PFNs are used to describe any physical page; this means
arch/arm64/kernel/head.S +13 -5
@@ -38,8 +38,6 @@ #include <asm/thread_info.h> #include <asm/virt.h> -#define __PHYS_OFFSET (KERNEL_START - TEXT_OFFSET) - #if (TEXT_OFFSET & 0xfff) != 0 #error TEXT_OFFSET must be at least 4KB aligned #elif (PAGE_OFFSET & 0x1fffff) != 0
@@ -50,6 +48,8 @@ #define KERNEL_START _text #define KERNEL_END _end +#define KERNEL_BASE (KERNEL_START - TEXT_OFFSET) + /* * Kernel startup entry point.
@@ -210,7 +210,15 @@ section_table: ENTRY(stext) bl preserve_boot_args bl el2_setup // Drop to EL1, w20=cpu_boot_mode - adrp x24, __PHYS_OFFSET + + /* + * Before the linear mapping has been set up, __va() translations will + * not produce usable virtual addresses unless we tweak PHYS_OFFSET to + * compensate for the offset between the kernel mapping and the base of + * the linear mapping. We will undo this in map_mem(). + */ + adrp x24, KERNEL_BASE + KIMAGE_OFFSET + bl set_cpu_boot_mode_flag bl __create_page_tables // x25=TTBR0, x26=TTBR1 /*
@@ -389,10 +397,10 @@ __create_page_tables: * Map the kernel image (starting with PHYS_OFFSET). */ mov x0, x26 // swapper_pg_dir - mov x5, #PAGE_OFFSET + ldr x5, =KERNEL_BASE create_pgd_entry x0, x5, x3, x6 ldr x6, =KERNEL_END // __va(KERNEL_END) - mov x3, x24 // phys offset + adrp x3, KERNEL_BASE // real PHYS_OFFSET create_block_map x0, x7, x3, x5, x6 /*
arch/arm64/kernel/vmlinux.lds.S +9 -2
@@ -7,6 +7,7 @@ #include <asm-generic/vmlinux.lds.h> #include <asm/kernel-pgtable.h> #include <asm/thread_info.h> +#include <asm/boot.h> #include <asm/memory.h> #include <asm/page.h> #include <asm/pgtable.h>
@@ -99,7 +100,7 @@ SECTIONS *(.discard.*) } - . = PAGE_OFFSET + TEXT_OFFSET; + . = KIMAGE_VADDR + TEXT_OFFSET; .head.text : { _text = .;
@@ -207,4 +208,10 @@ ASSERT(SIZEOF(.pgdir) <= ALIGNOF(.pgdir), ".pgdir size exceeds its alignment") /* * If padding is applied before .head.text, virt<->phys conversions will fail. */ -ASSERT(_text == (PAGE_OFFSET + TEXT_OFFSET), "HEAD is misaligned") +ASSERT(_text == (KIMAGE_VADDR + TEXT_OFFSET), "HEAD is misaligned") + +/* + * Make sure the memory footprint of the kernel Image does not exceed the limit. + */ +ASSERT(_end - _text + TEXT_OFFSET <= MAX_KIMG_SIZE, + "Kernel Image memory footprint exceeds MAX_KIMG_SIZE")
arch/arm64/mm/dump.c +2 -1
@@ -63,7 +63,8 @@ static struct addr_marker address_markers[] = { { PCI_IO_END, "PCI I/O end" }, { MODULES_VADDR, "Modules start" }, { MODULES_END, "Modules end" }, - { PAGE_OFFSET, "Kernel Mapping" }, + { KIMAGE_VADDR, "Kernel Mapping" }, + { PAGE_OFFSET, "Linear Mapping" }, { -1, NULL }, };
arch/arm64/mm/mmu.c +14 -5
@@ -51,6 +51,9 @@ u64 idmap_t0sz = TCR_T0SZ(VA_BITS);struct page *empty_zero_page;EXPORT_SYMBOL (empty_zero_page); +u64 kernel_va_offset__read_mostly ; +EXPORT_SYMBOL (kernel_va_offset); +pgprot_t phys_mem_access_prot (struct file *file,unsigned long pfn,unsigned long size,pgprot_t vma_prot) {
@@ -479,6 +482,9 @@ static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg,* Bootstrap the linear ranges that cover the start of DRAM and swapper_pg_dir * so that the statically allocated page tables as well as newly allocated ones * are accessible via the linear mapping. +* Since@this point, PHYS_OFFSET is still biased to redirect __va() +* translations into the kernel text mapping, we need to apply an +* explicit va_offset to calculate linear virtual addresses. */ static void __init bootstrap_linear_mapping (unsigned long va_offset) {
@@ -513,7 +519,10 @@ static void __init map_mem(void) {struct memblock_region *reg; -bootstrap_linear_mapping (0 ); +bootstrap_linear_mapping (KIMAGE_OFFSET ); + + kernel_va_offset =KIMAGE_OFFSET ; + memstart_addr -=KIMAGE_OFFSET ;/* map all the memory banks */ for_each_memblock (memory, reg) {
@@ -535,12 +544,12 @@ static void __init fixup_executable(void)#ifdef CONFIG_DEBUG_RODATA /* now that we are actually fully mapped, make the start/end more fine grained */ if (!IS_ALIGNED ((unsigned long )_stext,SWAPPER_BLOCK_SIZE )) { -unsigned long aligned_start =round_down (__pa (_stext), +unsigned long aligned_start =round_down ((unsigned long )_stext,SWAPPER_BLOCK_SIZE ); -create_mapping (aligned_start,__phys_to_virt (aligned_start), -__pa (_stext) - aligned_start, -PAGE_KERNEL ); +create_mapping (__pa (_stext), aligned_start, + (unsigned long )_stext - aligned_start, +PAGE_KERNEL ); }if (!IS_ALIGNED ((unsigned long )__init_end,SWAPPER_BLOCK_SIZE )) {
Signature · 1 line
1.9.1
Catalin Marinas
maintainerPATCH v3 5/7 arm64: move kernel mapping out of linear region
Quoted 4 lines · Ard Biesheuvel
arch/arm64/kernel/head.S +0
-0
[...]
arch/arm64/kernel/head.S section_table: · line 219
Quoted 12 lines
arch/arm64/kernel/head.S +7
-1@@ -210,7 +210,15 @@ section_table: ENTRY(stext) bl preserve_boot_args bl el2_setup // Drop to EL1, w20=cpu_boot_mode - adrp x24, __PHYS_OFFSET + + /* + * Before the linear mapping has been set up, __va() translations will + * not produce usable virtual addresses unless we tweak PHYS_OFFSET to + * compensate for the offset between the kernel mapping and the base of + * the linear mapping. We will undo this in map_mem(). + */
Minor typo in comment: I guess you meant "__pa() translations will not produce usable...".
arch/arm64/mm/dump.c static struct addr_marker address_markers[] = { · line 69
Quoted 13 lines
arch/arm64/mm/dump.c +2
-1@@ -63,7 +63,8 @@ static struct addr_marker address_markers[] = { { PCI_IO_END, "PCI I/O end" }, { MODULES_VADDR, "Modules start" }, { MODULES_END, "Modules end" }, - { PAGE_OFFSET, "Kernel Mapping" }, + { KIMAGE_VADDR, "Kernel Mapping" }, + { PAGE_OFFSET, "Linear Mapping" }, { -1, NULL }, };
Apart from this, please change the pr_notice() in mem_init() to show the linear mapping at the end (keep them in ascending order).
Signature · 1 line
Catalin
PATCH v3 5/7 arm64: move kernel mapping out of linear region
Quoted 6 lines · Catalin Marinas
Quoted 4 lines · Ard Biesheuvel
arch/arm64/kernel/head.S +0
-0[...]
Quoted 16 lines
arch/arm64/kernel/head.S section_table: · line 219
Quoted 12 lines
arch/arm64/kernel/head.S +7
-1@@ -210,7 +210,15 @@ section_table: ENTRY(stext) bl preserve_boot_args bl el2_setup // Drop to EL1, w20=cpu_boot_mode - adrp x24, __PHYS_OFFSET + + /* + * Before the linear mapping has been set up, __va() translations will + * not produce usable virtual addresses unless we tweak PHYS_OFFSET to + * compensate for the offset between the kernel mapping and the base of + * the linear mapping. We will undo this in map_mem(). + */Minor typo in comment: I guess you meant "
__pa()translations will not produce usable...".
No, not quite. __va() translations will normally produce addresses in the linear mapping, which will not be set up when we first start using it in create_mapping(). So until that time, we have to redirect __va() translations into the kernel mapping, where swapper_pg_dir is shadowed. I am hoping that Mark's planned changes to create_mapping() will make this unnecessary, but I haven't seen any of his code yet.
As far as __pa() is concerned, that translation is actually tweaked so it will always produce usable addresses, regardless of whether the bias is still set or not. The reason is that va-to-pa translations are always unambiguous.
Quoted 8 lines
Quoted 8 lines
arch/arm64/mm/dump.c +0
-0@@ -63,7 +63,8 @@ static struct addr_marker address_markers[] = { { PCI_IO_END, "PCI I/O end" }, { MODULES_VADDR, "Modules start" }, { MODULES_END, "Modules end" },
Quoted 9 lines
arch/arm64/mm/dump.c static struct addr_marker address_markers[] = { · line 69
Quoted 5 lines
arch/arm64/mm/dump.c +2
-1- { PAGE_OFFSET, "Kernel Mapping" }, + { KIMAGE_VADDR, "Kernel Mapping" }, + { PAGE_OFFSET, "Linear Mapping" }, { -1, NULL }, };Apart from this, please change the
pr_notice()inmem_init()to show the linear mapping at the end (keep them in ascending order).
OK
PATCH v3 5/7 arm64: move kernel mapping out of linear region
Quoted 31 lines · Ard Biesheuvel
Quoted 6 lines · Catalin Marinas
Quoted 4 lines · Ard Biesheuvel
arch/arm64/kernel/head.S +0
-0[...]
Quoted 16 lines
arch/arm64/kernel/head.S section_table: · line 219
Quoted 12 lines
arch/arm64/kernel/head.S +7
-1@@ -210,7 +210,15 @@ section_table: ENTRY(stext) bl preserve_boot_args bl el2_setup // Drop to EL1, w20=cpu_boot_mode - adrp x24, __PHYS_OFFSET + + /* + * Before the linear mapping has been set up, __va() translations will + * not produce usable virtual addresses unless we tweak PHYS_OFFSET to + * compensate for the offset between the kernel mapping and the base of + * the linear mapping. We will undo this in map_mem(). + */Minor typo in comment: I guess you meant "
__pa()translations will not produce usable...".No, not quite.
__va()translations will normally produce addresses in the linear mapping, which will not be set up when we first start using it increate_mapping(). So until that time, we have to redirect__va()translations into the kernel mapping, whereswapper_pg_diris shadowed. I am hoping that Mark's planned changes tocreate_mapping()will make this unnecessary, but I haven't seen any of his code yet.
Quoted 5 lines
As far as
__pa()is concerned, that translation is actually tweaked so it will always produce usable addresses, regardless of whether the bias is still set or not. The reason is that va-to-pa translations are always unambiguous.
... so of course, the comment is still wrong, -> s/virtual/physical/ addresses
Quoted 19 lines
Quoted 8 lines
Quoted 8 lines
arch/arm64/mm/dump.c +0
-0@@ -63,7 +63,8 @@ static struct addr_marker address_markers[] = { { PCI_IO_END, "PCI I/O end" }, { MODULES_VADDR, "Modules start" }, { MODULES_END, "Modules end" },Quoted 9 lines
arch/arm64/mm/dump.c static struct addr_marker address_markers[] = { · line 69
Quoted 5 lines
arch/arm64/mm/dump.c +2
-1- { PAGE_OFFSET, "Kernel Mapping" }, + { KIMAGE_VADDR, "Kernel Mapping" }, + { PAGE_OFFSET, "Linear Mapping" }, { -1, NULL }, };Apart from this, please change the
pr_notice()inmem_init()to show the linear mapping at the end (keep them in ascending order).OK
Catalin Marinas
maintainerPATCH v3 5/7 arm64: move kernel mapping out of linear region
Quoted 24 lines · Ard Biesheuvel
Quoted 23 lines · Ard Biesheuvel
Quoted 21 lines · Catalin Marinas
Quoted 4 lines · Ard Biesheuvel
arch/arm64/kernel/head.S +0
-0[...]
arch/arm64/kernel/head.S section_table: · line 219
Quoted 12 lines
arch/arm64/kernel/head.S +7
-1@@ -210,7 +210,15 @@ section_table: ENTRY(stext) bl preserve_boot_args bl el2_setup // Drop to EL1, w20=cpu_boot_mode - adrp x24, __PHYS_OFFSET + + /* + * Before the linear mapping has been set up, __va() translations will + * not produce usable virtual addresses unless we tweak PHYS_OFFSET to + * compensate for the offset between the kernel mapping and the base of + * the linear mapping. We will undo this in map_mem(). + */Minor typo in comment: I guess you meant "
__pa()translations will not produce usable...".
Quoted 5 lines
Quoted 5 lines
No, not quite.
__va()translations will normally produce addresses in the linear mapping, which will not be set up when we first start using it increate_mapping(). So until that time, we have to redirect__va()translations into the kernel mapping, whereswapper_pg_diris shadowed.
I guessed what you meant and I remember the reason based on past discussions, only that to me "linear mapping" sounds like something in virtual space while __va() generates a linear mapping -> physical translation (just some wording, nothing serious).
Quoted 2 lines
Quoted 2 lines
I am hoping that Mark's planned changes to
create_mapping()will make this unnecessary, but I haven't seen any of his code yet.
Not sure, I haven't seen the details yet.
Quoted 4 lines
Quoted 4 lines
As far as
__pa()is concerned, that translation is actually tweaked so it will always produce usable addresses, regardless of whether the bias is still set or not. The reason is that va-to-pa translations are always unambiguous.
Only that very early during boot memstart_addr is still based on the kernel load address rather than memblock_start_of_DRAM(), that's why I thought you meant __pa().
Quoted 2 lines
... so of course, the comment is still wrong, -> s/virtual/physical/ addresses
This would do.
Signature · 1 line
Catalin
PATCH v3 6/7 arm64: map linear region as non-executable
In reply to Ard BiesheuvelNow that we moved the kernel text out of the linear region, there is no longer a reason to map the linear region as executable. This also allows us to completely get rid of the __map_mem() variant that only maps some of it executable if CONFIG_DEBUG_RODATA is selected.
--- arch/arm64/mm/mmu.c | 41 +------------------- 1 file changed, 2 insertions(+), 39 deletions(-)
arch/arm64/mm/mmu.c +2 -39
@@ -357,47 +357,10 @@ static void create_mapping_late(phys_addr_t phys, unsigned long virt, phys, virt, size, prot, late_alloc); } -#ifdef CONFIG_DEBUG_RODATA static void __init __map_memblock (phys_addr_t start,phys_addr_t end) { -/* -* Set up the executable regions using the existing section mappings -* for now. This will get more fine grained later once all memory -* is mapped -*/ -unsigned long kernel_x_start =round_down (__pa (_stext),SWAPPER_BLOCK_SIZE ); -unsigned long kernel_x_end =round_up (__pa (__init_end),SWAPPER_BLOCK_SIZE ); - -if (end < kernel_x_start) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else if (start >= kernel_x_end) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else { -if (start < kernel_x_start) -create_mapping (start,__phys_to_virt (start), - kernel_x_start - start, -PAGE_KERNEL ); -create_mapping (kernel_x_start, -__phys_to_virt (kernel_x_start), - kernel_x_end - kernel_x_start, -PAGE_KERNEL_EXEC ); -if (kernel_x_end < end) -create_mapping (kernel_x_end, -__phys_to_virt (kernel_x_end), - end - kernel_x_end, -PAGE_KERNEL ); - } - -} -#else -static void __init __map_memblock (phys_addr_t start,phys_addr_t end) -{ -create_mapping (start,__phys_to_virt (start), end - start, -PAGE_KERNEL_EXEC ); +create_mapping (start,__phys_to_virt (start), end - start,PAGE_KERNEL ); } -#endif struct bootstrap_pgtables {pte_t pte[PTRS_PER_PTE ];
@@ -471,7 +434,7 @@ static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg, SWAPPER_BLOCK_SIZE));create_mapping (__pa (vstart - va_offset), vstart, vend - vstart, -PAGE_KERNEL_EXEC ); +PAGE_KERNEL );return vend; }
Signature · 1 line
1.9.1
Catalin Marinas
maintainerPATCH v3 6/7 arm64: map linear region as non-executable
Quoted 54 lines · Ard Biesheuvel
arch/arm64/mm/mmu.c +1
-38@@ -357,47 +357,10 @@ static void create_mapping_late(phys_addr_t phys, unsigned long virt, phys, virt, size, prot, late_alloc); } -#ifdef CONFIG_DEBUG_RODATA static void __init __map_memblock (phys_addr_t start,phys_addr_t end) { -/* -* Set up the executable regions using the existing section mappings -* for now. This will get more fine grained later once all memory -* is mapped -*/ -unsigned long kernel_x_start =round_down (__pa (_stext),SWAPPER_BLOCK_SIZE ); -unsigned long kernel_x_end =round_up (__pa (__init_end),SWAPPER_BLOCK_SIZE ); - -if (end < kernel_x_start) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else if (start >= kernel_x_end) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else { -if (start < kernel_x_start) -create_mapping (start,__phys_to_virt (start), - kernel_x_start - start, -PAGE_KERNEL ); -create_mapping (kernel_x_start, -__phys_to_virt (kernel_x_start), - kernel_x_end - kernel_x_start, -PAGE_KERNEL_EXEC ); -if (kernel_x_end < end) -create_mapping (kernel_x_end, -__phys_to_virt (kernel_x_end), - end - kernel_x_end, -PAGE_KERNEL ); - } - -} -#else -static void __init __map_memblock (phys_addr_t start,phys_addr_t end) -{ -create_mapping (start,__phys_to_virt (start), end - start, -PAGE_KERNEL_EXEC ); +create_mapping (start,__phys_to_virt (start), end - start,PAGE_KERNEL ); } -#endif struct bootstrap_pgtables {pte_t pte[PTRS_PER_PTE ];
arch/arm64/mm/mmu.c static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg, · line 440
Quoted 8 lines
arch/arm64/mm/mmu.c +1
-1@@ -471,7 +434,7 @@ static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg, SWAPPER_BLOCK_SIZE));create_mapping (__pa (vstart - va_offset), vstart, vend - vstart, -PAGE_KERNEL_EXEC ); +PAGE_KERNEL );return vend; }
These make sense. However, shall we go a step further and unmap the kernel image completely from the linear mapping, maybe based on CONFIG_DEBUG_RODATA? The mark_rodata_ro() function changes the text to read-only but you can still get writable access to it via __va(__pa(_stext)).
Signature · 1 line
Catalin
PATCH v3 6/7 arm64: map linear region as non-executable
Quoted 61 lines · Catalin Marinas
Quoted 60 lines · Ard Biesheuvel
arch/arm64/mm/mmu.c +2
-39@@ -357,47 +357,10 @@ static void create_mapping_late(phys_addr_t phys, unsigned long virt, phys, virt, size, prot, late_alloc); } -#ifdef CONFIG_DEBUG_RODATA static void __init __map_memblock (phys_addr_t start,phys_addr_t end) { -/* -* Set up the executable regions using the existing section mappings -* for now. This will get more fine grained later once all memory -* is mapped -*/ -unsigned long kernel_x_start =round_down (__pa (_stext),SWAPPER_BLOCK_SIZE ); -unsigned long kernel_x_end =round_up (__pa (__init_end),SWAPPER_BLOCK_SIZE ); - -if (end < kernel_x_start) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else if (start >= kernel_x_end) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else { -if (start < kernel_x_start) -create_mapping (start,__phys_to_virt (start), - kernel_x_start - start, -PAGE_KERNEL ); -create_mapping (kernel_x_start, -__phys_to_virt (kernel_x_start), - kernel_x_end - kernel_x_start, -PAGE_KERNEL_EXEC ); -if (kernel_x_end < end) -create_mapping (kernel_x_end, -__phys_to_virt (kernel_x_end), - end - kernel_x_end, -PAGE_KERNEL ); - } - -} -#else -static void __init __map_memblock (phys_addr_t start,phys_addr_t end) -{ -create_mapping (start,__phys_to_virt (start), end - start, -PAGE_KERNEL_EXEC ); +create_mapping (start,__phys_to_virt (start), end - start,PAGE_KERNEL ); } -#endif struct bootstrap_pgtables {pte_t pte[PTRS_PER_PTE ];@@ -471,7 +434,7 @@ static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg, SWAPPER_BLOCK_SIZE));create_mapping (__pa (vstart - va_offset), vstart, vend - vstart, -PAGE_KERNEL_EXEC ); +PAGE_KERNEL );
Quoted 9 lines
arch/arm64/mm/mmu.c static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg, · line 440
Quoted 2 lines
arch/arm64/mm/mmu.c +0
-0return vend; }These make sense. However, shall we go a step further and unmap the kernel image completely from the linear mapping, maybe based on
CONFIG_DEBUG_RODATA? Themark_rodata_ro()function changes the text to read-only but you can still get writable access to it via __va(__pa(_stext)).
If we can tolerate the fragmentation, then yes, let's unmap it completely. As long as we don't unmap the.pgdir section, since that will be referenced via the linear mapping
Catalin Marinas
maintainerPATCH v3 6/7 arm64: map linear region as non-executable
Quoted 71 lines · Ard Biesheuvel
Quoted 61 lines · Catalin Marinas
Quoted 60 lines · Ard Biesheuvel
arch/arm64/mm/mmu.c +2
-39@@ -357,47 +357,10 @@ static void create_mapping_late(phys_addr_t phys, unsigned long virt, phys, virt, size, prot, late_alloc); } -#ifdef CONFIG_DEBUG_RODATA static void __init __map_memblock (phys_addr_t start,phys_addr_t end) { -/* -* Set up the executable regions using the existing section mappings -* for now. This will get more fine grained later once all memory -* is mapped -*/ -unsigned long kernel_x_start =round_down (__pa (_stext),SWAPPER_BLOCK_SIZE ); -unsigned long kernel_x_end =round_up (__pa (__init_end),SWAPPER_BLOCK_SIZE ); - -if (end < kernel_x_start) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else if (start >= kernel_x_end) { -create_mapping (start,__phys_to_virt (start), - end - start,PAGE_KERNEL ); - }else { -if (start < kernel_x_start) -create_mapping (start,__phys_to_virt (start), - kernel_x_start - start, -PAGE_KERNEL ); -create_mapping (kernel_x_start, -__phys_to_virt (kernel_x_start), - kernel_x_end - kernel_x_start, -PAGE_KERNEL_EXEC ); -if (kernel_x_end < end) -create_mapping (kernel_x_end, -__phys_to_virt (kernel_x_end), - end - kernel_x_end, -PAGE_KERNEL ); - } - -} -#else -static void __init __map_memblock (phys_addr_t start,phys_addr_t end) -{ -create_mapping (start,__phys_to_virt (start), end - start, -PAGE_KERNEL_EXEC ); +create_mapping (start,__phys_to_virt (start), end - start,PAGE_KERNEL ); } -#endif struct bootstrap_pgtables {pte_t pte[PTRS_PER_PTE ];@@ -471,7 +434,7 @@ static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg, SWAPPER_BLOCK_SIZE));create_mapping (__pa (vstart - va_offset), vstart, vend - vstart, -PAGE_KERNEL_EXEC ); +PAGE_KERNEL );Quoted 8 lines
arch/arm64/mm/mmu.c static unsigned long __init bootstrap_region(struct bootstrap_pgtables *reg, · line 440
Quoted 2 lines
arch/arm64/mm/mmu.c +0
-0return vend; }These make sense. However, shall we go a step further and unmap the kernel image completely from the linear mapping, maybe based on
CONFIG_DEBUG_RODATA? Themark_rodata_ro()function changes the text to read-only but you can still get writable access to it via __va(__pa(_stext)).
Quoted 3 lines
If we can tolerate the fragmentation, then yes, let's unmap it completely. As long as we don't unmap the.pgdir section, since that will be referenced via the linear mapping
I think we should do this in mark_rodata_ro() function *if* CONFIG_DEBUG_RODATA is enabled, otherwise we leave them as they are (non-exec linear mapping).
The problem, as before is potential TLB conflicts that Mark is going to solve ;).
Signature · 1 line
Catalin
PATCH v3 7/7 arm64: allow kernel Image to be loaded anywhere in physical memory
In reply to Ard BiesheuvelThis relaxes the kernel Image placement requirements, so that it may be placed at any 2 MB aligned offset in physical memory.
This is accomplished by ignoring PHYS_OFFSET when installing memblocks, and accounting for the apparent virtual offset of the kernel Image (in addition to the 64 MB that it is moved below PAGE_OFFSET). As a result, virtual address references below PAGE_OFFSET are correctly mapped onto physical references into the kernel Image regardless of where it sits in memory.
--- Documentation/arm64/booting.txt | 12 ++--- arch/arm64/include/asm/memory.h | 9 ++-- arch/arm64/mm/init.c | 49 +++++++++++++++++++- arch/arm64/mm/mmu.c | 29 ++++++++++-- 4 files changed, 83 insertions(+), 16 deletions(-)
Documentation/arm64/booting.txt +6 -6
@@ -117,14 +117,14 @@ Header notes: depending on selected features, and is effectively unbound. The Image must be placed text_offset bytes from a 2MB aligned base -address near the start of usable system RAM and called there. Memory -below that base address is currently unusable by Linux, and therefore it -is strongly recommended that this location is the start of system RAM. -The region between the 2 MB aligned base address and the start of the -image has no special significance to the kernel, and may be used for -other purposes. +address anywhere in usable system RAM and called there. The region +between the 2 MB aligned base address and the start of the image has no +special significance to the kernel, and may be used for other purposes. At least image_size bytes from the start of the image must be free for use by the kernel. +NOTE: versions prior to v4.5 cannot make use of memory below the +physical offset of the Image so it is recommended that the Image be +placed as close as possible to the start of system RAM. Any memory described to the kernel (even that below the start of the image) which is not marked as reserved from the kernel (e.g., with a
arch/arm64/include/asm/memory.h +3 -6
@@ -120,13 +120,10 @@ extern phys_addr_t memstart_addr;extern u64 kernel_va_offset;/* -* The maximum physical address that the linear direct mapping -* of system RAM can cover. (PAGE_OFFSET can be interpreted as -* a 2's complement signed quantity and negated to derive the -* maximum size of the linear mapping.) +* Allow all memory at the discovery stage. We will clip it later. */ -#define MAX_MEMBLOCK_ADDR ({ memstart_addr - PAGE_OFFSET - 1; }) -#define MIN_MEMBLOCK_ADDR __pa(KIMAGE_VADDR) +#define MIN_MEMBLOCK_ADDR 0 +#define MAX_MEMBLOCK_ADDR U64_MAX/* * PFNs are used to describe any physical page; this means
arch/arm64/mm/init.c +48 -1
63 lines not on this page · raw message
arch/arm64/mm/mmu.c +26 -3
@@ -21,6 +21,7 @@#include <linux/kernel.h> #include <linux/errno.h> #include <linux/init.h> +#include <linux/initrd.h> #include <linux/libfdt.h> #include <linux/mman.h> #include <linux/nodemask.h>
@@ -481,11 +482,33 @@ static void __init bootstrap_linear_mapping(unsigned long va_offset)static void __init map_mem (void ) {struct memblock_region *reg; +u64 new_memstart_addr; +u64 new_va_offset; -bootstrap_linear_mapping (KIMAGE_OFFSET ); +/* +* Select a suitable value for the base of physical memory. +* This should be equal to or below the lowest usable physical +* memory address, and aligned to PUD/PMD size so that we can map +* it efficiently. +*/ + new_memstart_addr =round_down (memblock_start_of_DRAM (),SZ_1G ); + +/* +* Calculate the offset between the kernel text mapping that exists +* outside of the linear mapping, and its mapping in the linear region. +*/ + new_va_offset = memstart_addr - new_memstart_addr; + +bootstrap_linear_mapping (new_va_offset); - kernel_va_offset =KIMAGE_OFFSET ; - memstart_addr -=KIMAGE_OFFSET ; + kernel_va_offset = new_va_offset; + memstart_addr = new_memstart_addr; + +/* Recalculate virtual addresses of initrd region */ +if (IS_ENABLED (CONFIG_BLK_DEV_INITRD ) && initrd_start) { + initrd_start += new_va_offset; + initrd_end += new_va_offset; + }/* map all the memory banks */ for_each_memblock (memory, reg) {
Signature · 1 line
1.9.1
Catalin Marinas
maintainerPATCH v3 7/7 arm64: allow kernel Image to be loaded anywhere in physical memory
Quoted 8 lines · Ard Biesheuvel
arch/arm64/include/asm/memory.h +0
-0@@ -120,13 +120,10 @@ extern phys_addr_t memstart_addr;extern u64 kernel_va_offset;/*
arch/arm64/include/asm/memory.h extern phys_addr_t memstart_addr; · line 126
Quoted 10 lines
arch/arm64/include/asm/memory.h +3
-6-* The maximum physical address that the linear direct mapping -* of system RAM can cover. (PAGE_OFFSET can be interpreted as -* a 2's complement signed quantity and negated to derive the -* maximum size of the linear mapping.) +* Allow all memory at the discovery stage. We will clip it later. */ -#define MAX_MEMBLOCK_ADDR ({ memstart_addr - PAGE_OFFSET - 1; }) -#define MIN_MEMBLOCK_ADDR __pa(KIMAGE_VADDR) +#define MIN_MEMBLOCK_ADDR 0 +#define MAX_MEMBLOCK_ADDR U64_MAX
Just in case we get some random memblock information, shall we cap the maximum to PHYS_MASK?
Quoted 30 lines
arch/arm64/mm/init.c +22
-0@@ -158,9 +159,55 @@ static int __init early_mem(char *p) }early_param ("mem" , early_mem); +static void __init enforce_memory_limit (void ) +{ +const phys_addr_t kbase =round_down (__pa (_text),MIN_KIMG_ALIGN ); +u64 to_remove =memblock_phys_mem_size () - memory_limit; +phys_addr_t max_addr =0 ; +struct memblock_region *r; + +if (memory_limit == (phys_addr_t )ULLONG_MAX ) +return ; + +/* +* The kernel may be high up in physical memory, so try to apply the +* limit below the kernel first, and only let the generic handling +* take over if it turns out we haven't clipped enough memory yet. +*/ +for_each_memblock (memory, r) { +if (r->base + r->size > kbase) { +u64 rem =min (to_remove, kbase - r->base); + + max_addr = r->base + rem; + to_remove -= rem; +break ;
arch/arm64/mm/init.c static int __init early_mem(char *p) · line 193
Quoted 10 lines
arch/arm64/mm/init.c +10
-0+ } +if (to_remove <= r->size) { + max_addr = r->base + to_remove; + to_remove =0 ; +break ; + } + to_remove -= r->size; + } + +memblock_remove (0 , max_addr);
I don't fully get the reason for this function. Do you want to keep the kernel around in memblock? How do we guarantee that the call below wouldn't remove it anyway?
arch/arm64/mm/init.c static int __init early_mem(char *p) · line 196
Quoted 3 lines
arch/arm64/mm/init.c +3
-0+ +if (to_remove) +memblock_enforce_memory_limit (memory_limit);
Shouldn't this be memblock_enforce_memory_limit(to_remove)?
Quoted 10 lines
arch/arm64/mm/mmu.c +1
-0@@ -481,11 +482,33 @@ static void __init bootstrap_linear_mapping(unsigned long va_offset)static void __init map_mem (void ) {struct memblock_region *reg; +u64 new_memstart_addr;
arch/arm64/mm/mmu.c static void __init bootstrap_linear_mapping(unsigned long va_offset) · line 494
Quoted 10 lines
arch/arm64/mm/mmu.c +8
-1+u64 new_va_offset; -bootstrap_linear_mapping (KIMAGE_OFFSET ); +/* +* Select a suitable value for the base of physical memory. +* This should be equal to or below the lowest usable physical +* memory address, and aligned to PUD/PMD size so that we can map +* it efficiently. +*/ + new_memstart_addr =round_down (memblock_start_of_DRAM (),SZ_1G );
With this trick, we can no longer assume we have a mapping at PAGE_OFFSET. I don't think we break any expectations but we probably don't free the unused memmap at the beginning. We can probably set prev_end to this rounded down address in free_unused_memmap().
Signature · 1 line
Catalin
PATCH v3 7/7 arm64: allow kernel Image to be loaded anywhere in physical memory
Quoted 8 lines · Catalin Marinas
Quoted 7 lines · Ard Biesheuvel
arch/arm64/include/asm/memory.h +0
-0@@ -120,13 +120,10 @@ extern phys_addr_t memstart_addr;extern u64 kernel_va_offset;
Quoted 15 lines
arch/arm64/include/asm/memory.h extern phys_addr_t memstart_addr; · line 126
Quoted 11 lines
arch/arm64/include/asm/memory.h +3
-6/* -* The maximum physical address that the linear direct mapping -* of system RAM can cover. (PAGE_OFFSET can be interpreted as -* a 2's complement signed quantity and negated to derive the -* maximum size of the linear mapping.) +* Allow all memory at the discovery stage. We will clip it later. */ -#define MAX_MEMBLOCK_ADDR ({ memstart_addr - PAGE_OFFSET - 1; }) -#define MIN_MEMBLOCK_ADDR __pa(KIMAGE_VADDR) +#define MIN_MEMBLOCK_ADDR 0 +#define MAX_MEMBLOCK_ADDR U64_MAXJust in case we get some random memblock information, shall we cap the maximum to
PHYS_MASK?
Yes, that makes sense.
Quoted 36 lines
Quoted 36 lines
arch/arm64/mm/init.c +28
-0@@ -158,9 +159,55 @@ static int __init early_mem(char *p) }early_param ("mem" , early_mem); +static void __init enforce_memory_limit (void ) +{ +const phys_addr_t kbase =round_down (__pa (_text),MIN_KIMG_ALIGN ); +u64 to_remove =memblock_phys_mem_size () - memory_limit; +phys_addr_t max_addr =0 ; +struct memblock_region *r; + +if (memory_limit == (phys_addr_t )ULLONG_MAX ) +return ; + +/* +* The kernel may be high up in physical memory, so try to apply the +* limit below the kernel first, and only let the generic handling +* take over if it turns out we haven't clipped enough memory yet. +*/ +for_each_memblock (memory, r) { +if (r->base + r->size > kbase) { +u64 rem =min (to_remove, kbase - r->base); + + max_addr = r->base + rem; + to_remove -= rem; +break ; + } +if (to_remove <= r->size) { + max_addr = r->base + to_remove; + to_remove =0 ; +break ; + }
Quoted 9 lines
arch/arm64/mm/init.c static int __init early_mem(char *p) · line 193
Quoted 4 lines
arch/arm64/mm/init.c +4
-0+ to_remove -= r->size; + } + +memblock_remove (0 , max_addr);I don't fully get the reason for this function. Do you want to keep the kernel around in memblock? How do we guarantee that the call below wouldn't remove it anyway?
The problem is that the ordinary memblock_enforce_memory_limit() removes memory from the top, which means it will happily remove the memory that covers your kernel image if it happens to be loaded high up in physical memory.
Quoted 6 lines
arch/arm64/mm/init.c static int __init early_mem(char *p) · line 196
Quoted 3 lines
arch/arm64/mm/init.c +3
-0+ +if (to_remove) +memblock_enforce_memory_limit (memory_limit);Shouldn't this be
memblock_enforce_memory_limit(to_remove)?
No, it takes the memory limit as input. 'to_remove + memory_limit' will be exactly the remaining memory at this point.
Quoted 12 lines
Quoted 12 lines
arch/arm64/mm/mmu.c +2
-0@@ -481,11 +482,33 @@ static void __init bootstrap_linear_mapping(unsigned long va_offset)static void __init map_mem (void ) {struct memblock_region *reg; +u64 new_memstart_addr; +u64 new_va_offset;
Quoted 14 lines
arch/arm64/mm/mmu.c static void __init bootstrap_linear_mapping(unsigned long va_offset) · line 494
Quoted 8 lines
arch/arm64/mm/mmu.c +7
-1-bootstrap_linear_mapping (KIMAGE_OFFSET ); +/* +* Select a suitable value for the base of physical memory. +* This should be equal to or below the lowest usable physical +* memory address, and aligned to PUD/PMD size so that we can map +* it efficiently. +*/ + new_memstart_addr =round_down (memblock_start_of_DRAM (),SZ_1G );With this trick, we can no longer assume we have a mapping at
PAGE_OFFSET. I don't think we break any expectations but we probably don't free the unused memmap at the beginning. We can probably setprev_endto this rounded down address infree_unused_memmap().
I will look into that.
Catalin Marinas
maintainerPATCH v3 7/7 arm64: allow kernel Image to be loaded anywhere in physical memory
Quoted 47 lines · Ard Biesheuvel
Quoted 37 lines · Catalin Marinas
Quoted 36 lines · Ard Biesheuvel
arch/arm64/mm/init.c +28
-0@@ -158,9 +159,55 @@ static int __init early_mem(char *p) }early_param ("mem" , early_mem); +static void __init enforce_memory_limit (void ) +{ +const phys_addr_t kbase =round_down (__pa (_text),MIN_KIMG_ALIGN ); +u64 to_remove =memblock_phys_mem_size () - memory_limit; +phys_addr_t max_addr =0 ; +struct memblock_region *r; + +if (memory_limit == (phys_addr_t )ULLONG_MAX ) +return ; + +/* +* The kernel may be high up in physical memory, so try to apply the +* limit below the kernel first, and only let the generic handling +* take over if it turns out we haven't clipped enough memory yet. +*/ +for_each_memblock (memory, r) { +if (r->base + r->size > kbase) { +u64 rem =min (to_remove, kbase - r->base); + + max_addr = r->base + rem; + to_remove -= rem; +break ; + } +if (to_remove <= r->size) { + max_addr = r->base + to_remove; + to_remove =0 ; +break ; + }Quoted 8 lines
arch/arm64/mm/init.c static int __init early_mem(char *p) · line 193
Quoted 4 lines
arch/arm64/mm/init.c +4
-0+ to_remove -= r->size; + } + +memblock_remove (0 , max_addr);I don't fully get the reason for this function. Do you want to keep the kernel around in memblock? How do we guarantee that the call below wouldn't remove it anyway?
Quoted 4 lines
The problem is that the ordinary
memblock_enforce_memory_limit()removes memory from the top, which means it will happily remove the memory that covers your kernel image if it happens to be loaded high up in physical memory.
We could fix the memblock_reserve() call on the kernel image but apart from that we don't care about memblock's knowledge of the kernel text. A potential problem is freeing the init memory which assumes it's present in the linear mapping, though we could add additional checks here as well. Is memblock_end_of_DRAM() adjusted to the new maximum address after memblock_enforce_memory_limit()?
Quoted 8 lines
Quoted 5 lines
arch/arm64/mm/init.c static int __init early_mem(char *p) · line 196
Quoted 3 lines
arch/arm64/mm/init.c +3
-0+ +if (to_remove) +memblock_enforce_memory_limit (memory_limit);Shouldn't this be
memblock_enforce_memory_limit(to_remove)?No, it takes the memory limit as input. '
to_remove+memory_limit' will be exactly the remaining memory at this point.
You are right, I thought it's the memory to remove.
Signature · 1 line
Catalin