arm64: Add support for LPA2 and WXN at stage 1
Subsystems (6)
v7 cover arm64: Add support for LPA2 and WXN at stage 1
From: Ard Biesheuvel <[email protected]>
This v7 covers the remaining changes that implement support for LPA2 and WXN at stage 1, now that some of the prerequisites are in place.
Ryan's KVM series for LPA2 at stage 2 has been merged in the mean time, so the temporary changes to plug that hole have been dropped.
v4: https://lore.kernel.org/r/[email protected]/
v5: https://lore.kernel.org/r/[email protected]/
v6: https://lore.kernel.org/r/[email protected]/
-%-
Changes in v7:
- rebase onto v6.8-rc1 which includes some patches of the previous revision, and includes the KVM changes for LPA2
The first ~8 patches of this series rework how the kernel VA space is organized, so that the vmemmap region does not take up more space than necessary, and so that most of it can be reclaimed when running a build capable of 52-bit virtual addressing on hardware that is not. This is needed because the vmemmap region will take up a substantial part of the upper VA region that it shares with the kernel, modules and vmalloc/vmap mappings once we enable LPA2 with 4k pages.
The next ~30 patches rework the early init code, reimplementing most of the page table and relocation handling in C code. There are several reasons why this is beneficial:
- we generally prefer C code over asm for these things, and the macros that currently exist in head.S for creating the kernel pages tables are a good example why;
- we no longer need to create the kernel mapping in two passes, which means we can remove the logic that copies parts of the fixmap and the KAsan shadow from one set of page tables to the other; this is especially advantageous for KAsan with LPA2, which needs more elaborate shadow handling across multiple levels, since the KAsan region cannot be placed on exact pgd_t boundaries in that case;
- we can read the ID registers and parse command line overrides before creating the page tables, which simplifies the LPA2 case, as flicking the global TCR_EL1.DS bit at a later stage would require elaborate repainting of all page table descriptors, some of which with the MMU disabled;
- we can use more elaborate logic to create the mappings, which means we can use more precise mappings for code and data sections even when using 2 MiB granularity, and this is a prerequisite for running with WXN.
As part of the ID map changes, we decouple the ID map size from the kernel VA size, and switch to a 48-bit VA map for all configurations.
The next ~10 patches rework the existing LVA support as a CPU feature, which simplifies some code and gets rid of the vabits_actual variable. Then, LPA2 support is implemented in the same vein. This requires adding support for 5 level paging as well, given that LPA2 introduces a new paging level '-1' when using 4k pages.
Combined with the vmemmap changes at the start of the series, the resulting LPA2/4k pages configuration will have the exact same VA space layout as the ordinary 4k/4 levels configuration, and so LPA2 support can reasonably be enabled by default, as the fallback is seamless on non-LPA2 hardware.
In the 16k/LPA2 case, the fallback also reduces the number of paging levels, resulting in a 47-bit VA space. This is based on the assumption that hybrid LPA2/non-LPA2 16k pages kernels in production use would prefer not to take the performance hit of 4 level paging to gain only a single additional bit of VA space. (Note that generic Android kernels use only 3 levels of paging today.) Bespoke 16k configurations can still configure 48-bit virtual addressing as before.
Finally, enable support for running with the WXN control enabled. This was previously part of a separate series, but given that the delta is tiny, it is included here as well.
Ard Biesheuvel (49):
arm64: mm: Move PCI I/O emulation region above the vmemmap region
arm64: mm: Move fixmap region above vmemmap region
arm64: ptdump: Allow all region boundaries to be defined at boot time
arm64: ptdump: Discover start of vmemmap region at runtime
arm64: vmemmap: Avoid base2 order of struct page size to dimension
region
arm64: mm: Reclaim unused vmemmap region for vmalloc use
arm64: kaslr: Adjust randomization range dynamically
arm64: kernel: Manage absolute relocations in code built under pi/
arm64: kernel: Don't rely on objcopy to make code under pi/ __init
arm64: head: move relocation handling to C code
arm64: idreg-override: Move to early mini C runtime
arm64: kernel: Remove early fdt remap code
arm64: head: Clear BSS and the kernel page tables in one go
arm64: Move feature overrides into the BSS section
arm64: head: Run feature override detection before mapping the kernel
arm64: head: move dynamic shadow call stack patching into early C
runtime
arm64: cpufeature: Add helper to test for CPU feature overrides
arm64: kaslr: Use feature override instead of parsing the cmdline
again
arm64: idreg-override: Create a pseudo feature for rodata=off
arm64: Add helpers to probe local CPU for PAC and BTI support
arm64: head: allocate more pages for the kernel mapping
arm64: head: move memstart_offset_seed handling to C code
arm64: mm: Make kaslr_requires_kpti() a static inline
arm64: mmu: Make __cpu_replace_ttbr1() out of line
arm64: head: Move early kernel mapping routines into C code
arm64: mm: Use 48-bit virtual addressing for the permanent ID map
arm64: pgtable: Decouple PGDIR size macros from PGD/PUD/PMD levels
arm64: kernel: Create initial ID map from C code
arm64: mm: avoid fixmap for early swapper_pg_dir updates
arm64: mm: omit redundant remap of kernel image
arm64: Revert "mm: provide idmap pointer to cpu_replace_ttbr1()"
arm64: mm: Handle LVA support as a CPU feature
arm64: mm: Add feature override support for LVA
arm64: Avoid #define'ing PTE_MAYBE_NG to 0x0 for asm use
arm64: Add ESR decoding for exceptions involving translation level -1
arm64: mm: Wire up TCR.DS bit to PTE shareability fields
arm64: mm: Add LPA2 support to phys<->pte conversion routines
arm64: mm: Add definitions to support 5 levels of paging
arm64: mm: add LPA2 and 5 level paging support to G-to-nG conversion
arm64: Enable LPA2 at boot if supported by the system
arm64: mm: Add 5 level paging support to fixmap and swapper handling
arm64: kasan: Reduce minimum shadow alignment and enable 5 level
paging
arm64: mm: Add support for folding PUDs at runtime
arm64: ptdump: Disregard unaddressable VA space
arm64: ptdump: Deal with translation levels folded at runtime
arm64: Enable 52-bit virtual addressing for 4k and 16k granule configs
arm64: defconfig: Enable LPA2 support
mm: add arch hook to validate mmap() prot flags
arm64: mm: add support for WXN memory translation attribute
Catalin Marinas (1):
arm64: Set the default CONFIG_ARM64_VA_BITS_52 in Kconfig rather than
defconfig
arch/arm64/Kconfig | 38 +-
arch/arm64/configs/defconfig | 1 -
arch/arm64/include/asm/archrandom.h | 2 -
arch/arm64/include/asm/assembler.h | 55 +--
arch/arm64/include/asm/cpufeature.h | 116 +++++
arch/arm64/include/asm/esr.h | 13 +-
arch/arm64/include/asm/fixmap.h | 2 +-
arch/arm64/include/asm/kasan.h | 2 -
arch/arm64/include/asm/kernel-pgtable.h | 103 ++---
arch/arm64/include/asm/kvm_emulate.h | 10 +-
arch/arm64/include/asm/memory.h | 31 +-
arch/arm64/include/asm/mman.h | 36 ++
arch/arm64/include/asm/mmu.h | 40 +-
arch/arm64/include/asm/mmu_context.h | 83 ++--
arch/arm64/include/asm/pgalloc.h | 53 ++-
arch/arm64/include/asm/pgtable-hwdef.h | 33 +-
arch/arm64/include/asm/pgtable-prot.h | 20 +-
arch/arm64/include/asm/pgtable-types.h | 6 +
arch/arm64/include/asm/pgtable.h | 229 +++++++++-
arch/arm64/include/asm/scs.h | 36 +-
arch/arm64/include/asm/setup.h | 3 -
arch/arm64/include/asm/tlb.h | 3 +
arch/arm64/kernel/Makefile | 13 +-
arch/arm64/kernel/cpufeature.c | 111 +++--
arch/arm64/kernel/head.S | 463 ++------------------
arch/arm64/kernel/image-vars.h | 35 ++
arch/arm64/kernel/kaslr.c | 4 +-
arch/arm64/kernel/module.c | 2 +-
arch/arm64/kernel/pi/Makefile | 27 +-
arch/arm64/kernel/{ => pi}/idreg-override.c | 80 ++--
arch/arm64/kernel/pi/kaslr_early.c | 78 +---
arch/arm64/kernel/pi/map_kernel.c | 276 ++++++++++++
arch/arm64/kernel/pi/map_range.c | 105 +++++
arch/arm64/kernel/{ => pi}/patch-scs.c | 36 +-
arch/arm64/kernel/pi/pi.h | 36 ++
arch/arm64/kernel/pi/relacheck.c | 130 ++++++
arch/arm64/kernel/pi/relocate.c | 64 +++
arch/arm64/kernel/setup.c | 22 -
arch/arm64/kernel/sleep.S | 3 -
arch/arm64/kernel/vmlinux.lds.S | 17 +-
arch/arm64/kvm/mmu.c | 15 +-
arch/arm64/mm/fault.c | 30 +-
arch/arm64/mm/fixmap.c | 39 +-
arch/arm64/mm/init.c | 2 +-
arch/arm64/mm/kasan_init.c | 159 +++++--
arch/arm64/mm/mmap.c | 4 +
arch/arm64/mm/mmu.c | 255 ++++++-----
arch/arm64/mm/pgd.c | 17 +-
arch/arm64/mm/proc.S | 122 +++++-
arch/arm64/mm/ptdump.c | 77 ++--
arch/arm64/tools/cpucaps | 1 +
include/linux/mman.h | 15 +
mm/mmap.c | 3 +
53 files changed, 1995 insertions(+), 1161 deletions(-)
rename arch/arm64/kernel/{ => pi}/idreg-override.c (83%)
create mode 100644 arch/arm64/kernel/pi/map_kernel.c
create mode 100644 arch/arm64/kernel/pi/map_range.c
rename arch/arm64/kernel/{ => pi}/patch-scs.c (89%)
create mode 100644 arch/arm64/kernel/pi/pi.h
create mode 100644 arch/arm64/kernel/pi/relacheck.c
create mode 100644 arch/arm64/kernel/pi/relocate.cSignature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 1/50 arm64: mm: Move PCI I/O emulation region above the vmemmap region
From: Ard Biesheuvel <[email protected]>
Move the PCI I/O region above the vmemmap region in the kernel's VA space. This will permit us to reclaim the lower part of the vmemmap region for vmalloc/vmap allocations when running a 52-bit VA capable build on a 48-bit VA capable system.
Also, given that PCI_IO_START is derived from VMEMMAP_END, use that symbolic constant directly in ptdump rather than deriving it from VMEMMAP_START and VMEMMAP_SIZE, as those definitions will change in subsequent patches.
--- arch/arm64/include/asm/memory.h | 4 ++-- arch/arm64/mm/ptdump.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-)
arch/arm64/include/asm/memory.h +2 -2
@@ -49,8 +49,8 @@#define MODULES_VSIZE (SZ_2G)#define VMEMMAP_START (-(UL(1) << (VA_BITS - VMEMMAP_SHIFT)))#define VMEMMAP_END (VMEMMAP_START + VMEMMAP_SIZE) -#define PCI_IO_END (VMEMMAP_START - SZ_8M) -#define PCI_IO_START (PCI_IO_END - PCI_IO_SIZE) +#define PCI_IO_START (VMEMMAP_END + SZ_8M) +#define PCI_IO_END (PCI_IO_START + PCI_IO_SIZE)#define FIXADDR_TOP (VMEMMAP_START - SZ_32M)#if VA_BITS >48
arch/arm64/mm/ptdump.c +2 -2
@@ -47,10 +47,10 @@ static struct addr_marker address_markers[] = { { VMALLOC_END, "vmalloc() end" }, { FIXADDR_TOT_START, "Fixmap start" }, { FIXADDR_TOP, "Fixmap end" }, + { VMEMMAP_START, "vmemmap start" }, + { VMEMMAP_END, "vmemmap end" }, { PCI_IO_START, "PCI I/O start" }, { PCI_IO_END, "PCI I/O end" }, - { VMEMMAP_START, "vmemmap start" }, - { VMEMMAP_START + VMEMMAP_SIZE, "vmemmap end" }, { -1, NULL }, };
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 2/50 arm64: mm: Move fixmap region above vmemmap region
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Move the fixmap region above the vmemmap region, so that the start of the vmemmap delineates the end of the region available for vmalloc and vmap allocations and the randomized placement of the kernel and modules.
In a subsequent patch, we will take advantage of this to reclaim most of the vmemmap area when running a 52-bit VA capable build with 52-bit virtual addressing disabled at runtime.
Note that the existing guard region of 256 MiB covers the fixmap and PCI I/O regions as well, so we can reduce it 8 MiB, which is what we use in other places too.
--- arch/arm64/include/asm/memory.h | 2 +- arch/arm64/include/asm/pgtable.h | 2 +- arch/arm64/mm/fixmap.c | 3 +++ arch/arm64/mm/ptdump.c | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-)
arch/arm64/include/asm/memory.h +1 -1
@@ -51,7 +51,7 @@#define VMEMMAP_END (VMEMMAP_START + VMEMMAP_SIZE)#define PCI_IO_START (VMEMMAP_END + SZ_8M)#define PCI_IO_END (PCI_IO_START + PCI_IO_SIZE) -#define FIXADDR_TOP (VMEMMAP_START - SZ_32M) +#define FIXADDR_TOP (-UL(SZ_8M))#if VA_BITS >48 #define VA_BITS_MIN (48)
arch/arm64/include/asm/pgtable.h +1 -1
@@ -22,7 +22,7 @@* and fixed mappings */ #define VMALLOC_START (MODULES_END) -#define VMALLOC_END (VMEMMAP_START - SZ_256M) +#define VMALLOC_END (VMEMMAP_START - SZ_8M)#define vmemmap ((struct page *)VMEMMAP_START - (memstart_addr >> PAGE_SHIFT))
arch/arm64/mm/fixmap.c +3 -0
@@ -16,6 +16,9 @@#include <asm/pgalloc.h> #include <asm/tlbflush.h> +/* ensure that the fixmap region does not grow down into the PCI I/O region */ +static_assert (FIXADDR_TOT_START >PCI_IO_END ); +#define NR_BM_PTE_TABLES \ SPAN_NR_ENTRIES(FIXADDR_TOT_START, FIXADDR_TOP, PMD_SHIFT)#define NR_BM_PMD_TABLES \
arch/arm64/mm/ptdump.c +2 -2
@@ -45,12 +45,12 @@ static struct addr_marker address_markers[] = { { MODULES_END, "Modules end" }, { VMALLOC_START, "vmalloc() area" }, { VMALLOC_END, "vmalloc() end" }, - { FIXADDR_TOT_START, "Fixmap start" }, - { FIXADDR_TOP, "Fixmap end" }, { VMEMMAP_START, "vmemmap start" }, { VMEMMAP_END, "vmemmap end" }, { PCI_IO_START, "PCI I/O start" }, { PCI_IO_END, "PCI I/O end" }, + { FIXADDR_TOT_START, "Fixmap start" }, + { FIXADDR_TOP, "Fixmap end" }, { -1, NULL }, };
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 3/50 arm64: ptdump: Allow all region boundaries to be defined at boot time
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Rework the way the address_markers array is populated so that we can tolerate values that are not compile time constants generally, rather than keeping track manually of the array indexes in question, and poking new values into them manually. This will be needed for VMALLOC_END, which will cease to be a compile time constant after a subsequent patch.
--- arch/arm64/mm/ptdump.c | 54 ++++++++------------ 1 file changed, 22 insertions(+), 32 deletions(-)
arch/arm64/mm/ptdump.c +22 -32
@@ -26,34 +26,6 @@#include <asm/ptdump.h> -enum address_markers_idx { -PAGE_OFFSET_NR =0 , -PAGE_END_NR , -#if defined(CONFIG_KASAN_GENERIC ) || defined(CONFIG_KASAN_SW_TAGS ) -KASAN_START_NR , -#endif -}; - -static struct addr_marker address_markers[] = { - {PAGE_OFFSET ,"Linear Mapping start" }, - {0 /* PAGE_END */ ,"Linear Mapping end" }, -#if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) - {0 /* KASAN_SHADOW_START */ ,"Kasan shadow start" }, - {KASAN_SHADOW_END ,"Kasan shadow end" }, -#endif - {MODULES_VADDR ,"Modules start" }, - {MODULES_END ,"Modules end" }, - {VMALLOC_START ,"vmalloc() area" }, - {VMALLOC_END ,"vmalloc() end" }, - {VMEMMAP_START ,"vmemmap start" }, - {VMEMMAP_END ,"vmemmap end" }, - {PCI_IO_START ,"PCI I/O start" }, - {PCI_IO_END ,"PCI I/O end" }, - {FIXADDR_TOT_START ,"Fixmap start" }, - {FIXADDR_TOP ,"Fixmap end" }, - {-1 ,NULL }, -}; - #define pt_dump_seq_printf(m, fmt, args...) \ ({ \ if (m) \
@@ -339,9 +311,8 @@ static void __init ptdump_initialize(void) pg_level[i].mask |= pg_level[i].bits[j].mask; } -static struct ptdump_info kernel_ptdump_info = { +static struct ptdump_info kernel_ptdump_info__ro_after_init = { .mm = &init_mm, - .markers = address_markers, .base_addr =PAGE_OFFSET , };
@@ -375,10 +346,29 @@ void ptdump_check_wx(void)static int __init ptdump_init (void ) { - address_markers[PAGE_END_NR ].start_address =PAGE_END ; +struct addr_marker m[] = { + {PAGE_OFFSET ,"Linear Mapping start" }, + {PAGE_END ,"Linear Mapping end" }, #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) - address_markers[KASAN_START_NR ].start_address =KASAN_SHADOW_START ; + {KASAN_SHADOW_START ,"Kasan shadow start" }, + {KASAN_SHADOW_END ,"Kasan shadow end" }, #endif + {MODULES_VADDR ,"Modules start" }, + {MODULES_END ,"Modules end" }, + {VMALLOC_START ,"vmalloc() area" }, + {VMALLOC_END ,"vmalloc() end" }, + {VMEMMAP_START ,"vmemmap start" }, + {VMEMMAP_END ,"vmemmap end" }, + {PCI_IO_START ,"PCI I/O start" }, + {PCI_IO_END ,"PCI I/O end" }, + {FIXADDR_TOT_START ,"Fixmap start" }, + {FIXADDR_TOP ,"Fixmap end" }, + {-1 ,NULL }, + }; +static struct addr_marker address_markers[ARRAY_SIZE (m)]__ro_after_init ; + + kernel_ptdump_info.markers =memcpy (address_markers, m,sizeof (m)); +ptdump_initialize ();ptdump_debugfs_register (&kernel_ptdump_info,"kernel_page_tables" );return 0 ;
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 4/50 arm64: ptdump: Discover start of vmemmap region at runtime
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
We will soon reclaim the part of the vmemmap region that covers VA space that is not addressable by the hardware. To avoid confusion, ensure that the 'vmemmap start' marker points at the start of the region that is actually being used for the struct page array, rather than the start of the region we set aside for it at build time.
--- arch/arm64/mm/ptdump.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
arch/arm64/mm/ptdump.c +3 -1
@@ -346,6 +346,8 @@ void ptdump_check_wx(void)static int __init ptdump_init (void ) { +u64 page_offset =_PAGE_OFFSET (vabits_actual); +u64 vmemmap_start = (u64 )virt_to_page ((void *)page_offset);struct addr_marker m[] = { {PAGE_OFFSET ,"Linear Mapping start" }, {PAGE_END ,"Linear Mapping end" },
@@ -357,7 +359,7 @@ static int __init ptdump_init(void) { MODULES_END, "Modules end" }, { VMALLOC_START, "vmalloc() area" }, { VMALLOC_END, "vmalloc() end" }, - { VMEMMAP_START, "vmemmap start" }, + { vmemmap_start, "vmemmap start" }, { VMEMMAP_END, "vmemmap end" }, { PCI_IO_START, "PCI I/O start" }, { PCI_IO_END, "PCI I/O end" },
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 5/50 arm64: vmemmap: Avoid base2 order of struct page size to dimension region
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The placement and size of the vmemmap region in the kernel virtual address space is currently derived from the base2 order of the size of a struct page. This makes for nicely aligned constants with lots of leading 0xf and trailing 0x0 digits, but given that the actual struct pages are indexed as an ordinary array, this resulting region is severely overdimensioned when the size of a struct page is just over a power of 2.
This doesn't matter today, but once we enable 52-bit virtual addressing for 4k pages configurations, the vmemmap region may take up almost half of the upper VA region with the current struct page upper bound at 64 bytes. And once we enable KMSAN or other features that push the size of a struct page over 64 bytes, we will run out of VMALLOC space entirely.
So instead, let's derive the region size from the actual size of a struct page, and place the entire region 1 GB from the top of the VA space, where it still doesn't share any lower level translation table entries with the fixmap.
--- arch/arm64/include/asm/memory.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
arch/arm64/include/asm/memory.h +4 -4
@@ -30,8 +30,8 @@* keep a constant PAGE_OFFSET and "fallback" to using the higher end * of the VMEMMAP where 52-bit support is not available in hardware. */ -#define VMEMMAP_SHIFT (PAGE_SHIFT - STRUCT_PAGE_MAX_SHIFT) -#define VMEMMAP_SIZE ((_PAGE_END(VA_BITS_MIN) - PAGE_OFFSET) >> VMEMMAP_SHIFT) +#define VMEMMAP_RANGE (_PAGE_END(VA_BITS_MIN) - PAGE_OFFSET) +#define VMEMMAP_SIZE ((VMEMMAP_RANGE >> PAGE_SHIFT) * sizeof(struct page))/* * PAGE_OFFSET - the virtual address of the start of the linear map, at the
@@ -47,8 +47,8 @@#define MODULES_END (MODULES_VADDR + MODULES_VSIZE)#define MODULES_VADDR (_PAGE_END(VA_BITS_MIN))#define MODULES_VSIZE (SZ_2G) -#define VMEMMAP_START (-(UL(1) << (VA_BITS - VMEMMAP_SHIFT))) -#define VMEMMAP_END (VMEMMAP_START + VMEMMAP_SIZE) +#define VMEMMAP_START (VMEMMAP_END - VMEMMAP_SIZE) +#define VMEMMAP_END (-UL(SZ_1G))#define PCI_IO_START (VMEMMAP_END + SZ_8M)#define PCI_IO_END (PCI_IO_START + PCI_IO_SIZE)#define FIXADDR_TOP (-UL(SZ_8M))
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 6/50 arm64: mm: Reclaim unused vmemmap region for vmalloc use
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The vmemmap array is statically sized based on the maximum supported size of the virtual address space, but it is located inside the upper VA region, which is statically sized based on the *minimum* supported size of the VA space. This doesn't matter much when using 64k pages, which is the only configuration that currently supports 52-bit virtual addressing.
However, upcoming LPA2 support will change this picture somewhat, as in that case, the vmemmap array will take up more than 25% of the upper VA region when using 4k pages. Given that most of this space is never used when running on a system that does not support 52-bit virtual addressing, let's reclaim the unused vmemmap area in that case.
--- arch/arm64/include/asm/pgtable.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
arch/arm64/include/asm/pgtable.h +6 -2
@@ -18,11 +18,15 @@* VMALLOC range. * * VMALLOC_START: beginning of the kernel vmalloc space -* VMALLOC_END: extends to the available space below vmemmap, PCI I/O space -* and fixed mappings +* VMALLOC_END: extends to the available space below vmemmap */ #define VMALLOC_START (MODULES_END) +#if VA_BITS ==VA_BITS_MIN #define VMALLOC_END (VMEMMAP_START - SZ_8M) +#else +#define VMEMMAP_UNUSED_NPAGES ((_PAGE_OFFSET(vabits_actual) - PAGE_OFFSET) >> PAGE_SHIFT) +#define VMALLOC_END (VMEMMAP_START + VMEMMAP_UNUSED_NPAGES * sizeof(struct page) - SZ_8M) +#endif #define vmemmap ((struct page *)VMEMMAP_START - (memstart_addr >> PAGE_SHIFT))
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 7/50 arm64: kaslr: Adjust randomization range dynamically
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Currently, we base the KASLR randomization range on a rough estimate of the available space in the upper VA region: the lower 1/4th has the module region and the upper 1/4th has the fixmap, vmemmap and PCI I/O ranges, and so we pick a random location in the remaining space in the middle.
Once we enable support for 5-level paging with 4k pages, this no longer
works: the vmemmap region, being dimensioned to cover a 52-bit linear region, takes up so much space in the upper VA region (the size of which is based on a 48-bit VA space for compatibility with non-LVA hardware) that the region above the vmalloc region takes up more than a quarter of the available space.
So instead of a heuristic, let's derive the randomization range from the actual boundaries of the vmalloc region.
--- arch/arm64/kernel/image-vars.h | 2 ++ arch/arm64/kernel/pi/kaslr_early.c | 11 ++++++----- 2 files changed, 8 insertions(+), 5 deletions(-)
arch/arm64/kernel/image-vars.h +2 -0
@@ -36,6 +36,8 @@ PROVIDE(__pi___memcpy = __pi_memcpy);PROVIDE (__pi___memmove = __pi_memmove);PROVIDE (__pi___memset = __pi_memset); +PROVIDE (__pi_vabits_actual = vabits_actual); +#ifdef CONFIG_KVM /*
arch/arm64/kernel/pi/kaslr_early.c +6 -5
@@ -14,6 +14,7 @@#include <asm/archrandom.h> #include <asm/memory.h> +#include <asm/pgtable.h> /* taken from lib/string.c */ static char *__strstr (const char *s1,const char *s2)
@@ -87,7 +88,7 @@ static u64 get_kaslr_seed(void *fdt) asmlinkage u64 kaslr_early_init(void *fdt) { -u64 seed; +u64 seed, range;if (is_kaslr_disabled_cmdline (fdt))return 0 ;
@@ -102,9 +103,9 @@ asmlinkage u64 kaslr_early_init(void *fdt)/* * OK, so we are proceeding with KASLR enabled. Calculate a suitable * kernel image offset from the seed. Let's place the kernel in the -* middle half of the VMALLOC area (VA_BITS_MIN - 2), and stay clear of -* the lower and upper quarters to avoid colliding with other -* allocations. +* 'middle' half of the VMALLOC area, and stay clear of the lower and +* upper quarters to avoid colliding with other allocations. */ -return BIT (VA_BITS_MIN -3 ) + (seed &GENMASK (VA_BITS_MIN -3 ,0 )); + range = (VMALLOC_END -KIMAGE_VADDR ) /2 ; +return range /2 + (((__uint128_t )range * seed) >>64 ); }
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 8/50 arm64: kernel: Manage absolute relocations in code built under pi/
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The mini C runtime runs before relocations are processed, and so it cannot rely on statically initialized pointer variables.
Add a check to ensure that such code does not get introduced by accident, by going over the relocations in each object, identifying the ones that operate on data sections that are part of the executable image, and raising an error if any relocations of type R_AARCH64_ABS64 exist. Note that such relocations are permitted in other places (e.g., debug sections) and will never occur in compiler generated code sections when using the small code model, so only check sections that have SHF_ALLOC set and SHF_EXECINSTR cleared.
To accommodate cases where statically initialized symbol references are unavoidable, introduce a special case for ELF input data sections that have ".rodata.prel64" in their names, and in these cases, instead of rejecting any encountered ABS64 relocations, convert them into PREL64 relocations, which don't require any runtime fixups. Note that the code in question must still be modified to deal with this, as it needs to convert the 64-bit signed offsets into absolute addresses before use.
--- arch/arm64/kernel/pi/Makefile | 9 +- arch/arm64/kernel/pi/pi.h | 18 +++ arch/arm64/kernel/pi/relacheck.c | 130 ++++++++++++++++++++ 3 files changed, 155 insertions(+), 2 deletions(-)
arch/arm64/kernel/pi/Makefile +7 -2
@@ -22,11 +22,16 @@ KCSAN_SANITIZE := n UBSAN_SANITIZE := n KCOV_INSTRUMENT := n +hostprogs := relacheck + +quiet_cmd_piobjcopy = $(quiet_cmd_objcopy) + cmd_piobjcopy = $(cmd_objcopy) && $(obj)/relacheck $(@) $(<) + $(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \ --remove-section=.note.gnu.property \ --prefix-alloc-sections=.init -$(obj)/%.pi.o: $(obj)/%.o FORCE - $(call if_changed,objcopy) +$(obj)/%.pi.o: $(obj)/%.o $(obj)/relacheck FORCE + $(call if_changed,piobjcopy) $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c)
arch/arm64/kernel/pi/pi.h new file +18 -0
@@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2023 Google LLC +// Author: Ard Biesheuvel <[email protected]> + +#define __prel64_initconst __section(".init.rodata.prel64") + +#define PREL64 (type, name) union { type *name; prel64_t name ## _prel; } + +#define prel64_pointer (__d) (typeof(__d))prel64_to_pointer(&__d##_prel) + +typedef volatile signed long prel64_t ; + +static inline void *prel64_to_pointer (const prel64_t *offset) +{ +if (!*offset) +return NULL ; +return (void *)offset + *offset; +}
arch/arm64/kernel/pi/relacheck.c new file +130 -0
130 lines not on this page · raw message
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 9/50 arm64: kernel: Don't rely on objcopy to make code under pi/ __init
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
We will add some code under pi/ that contains global variables that should not end up in __initdata, as they will not be writable via the initial ID map. So only rely on objcopy for making the libfdt code __init, and use explicit annotations for the rest.
--- arch/arm64/kernel/pi/Makefile | 6 ++++-- arch/arm64/kernel/pi/kaslr_early.c | 16 +++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-)
arch/arm64/kernel/pi/Makefile +4 -2
@@ -28,11 +28,13 @@ quiet_cmd_piobjcopy = $(quiet_cmd_objcopy) cmd_piobjcopy = $(cmd_objcopy) && $(obj)/relacheck $(@) $(<) $(obj)/%.pi.o: OBJCOPYFLAGS := --prefix-symbols=__pi_ \ - --remove-section=.note.gnu.property \ - --prefix-alloc-sections=.init + --remove-section=.note.gnu.property $(obj)/%.pi.o: $(obj)/%.o $(obj)/relacheck FORCE $(call if_changed,piobjcopy) +# ensure that all the lib- code ends up as __init code and data +$(obj)/lib-%.pi.o: OBJCOPYFLAGS += --prefix-alloc-sections=.init + $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c)
arch/arm64/kernel/pi/kaslr_early.c +9 -7
@@ -17,7 +17,7 @@#include <asm/pgtable.h> /* taken from lib/string.c */ -static char *__strstr (const char *s1,const char *s2) +static char *__init __strstr (const char *s1,const char *s2) {size_t l1, l2;
@@ -33,7 +33,7 @@ static char *__strstr(const char *s1, const char *s2) }return NULL ; } -static bool cmdline_contains_nokaslr (const u8 *cmdline) +static bool __init cmdline_contains_nokaslr (const u8 *cmdline) {const u8 *str;
@@ -41,7 +41,7 @@ static bool cmdline_contains_nokaslr(const u8 *cmdline)return str == cmdline || (str > cmdline && *(str -1 ) ==' ' ); } -static bool is_kaslr_disabled_cmdline (void *fdt) +static bool __init is_kaslr_disabled_cmdline (void *fdt) {if (!IS_ENABLED (CONFIG_CMDLINE_FORCE )) {int node;
@@ -67,17 +67,19 @@ static bool is_kaslr_disabled_cmdline(void *fdt)return cmdline_contains_nokaslr (CONFIG_CMDLINE ); } -static u64 get_kaslr_seed (void *fdt) +static u64 __init get_kaslr_seed (void *fdt) { +static char const chosen_str[]__initconst ="chosen" ; +static char const seed_str[]__initconst ="kaslr-seed" ;int node, len;fdt64_t *prop;u64 ret; - node =fdt_path_offset (fdt,"/chosen" ); + node =fdt_path_offset (fdt, chosen_str);if (node <0 )return 0 ; - prop =fdt_getprop_w (fdt, node,"kaslr-seed" , &len); + prop =fdt_getprop_w (fdt, node, seed_str, &len);if (!prop || len !=sizeof (u64))return 0 ;
@@ -86,7 +88,7 @@ static u64 get_kaslr_seed(void *fdt)return ret; } -asmlinkage u64 kaslr_early_init(void *fdt) +asmlinkage u64 __init kaslr_early_init(void *fdt) {u64 seed, range;
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 10/50 arm64: head: move relocation handling to C code
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Now that we have a mini C runtime before the kernel mapping is up, we can move the non-trivial relocation processing code out of head.S and reimplement it in C.
--- arch/arm64/kernel/Makefile | 3 +- arch/arm64/kernel/head.S | 104 ++------------------ arch/arm64/kernel/pi/Makefile | 5 +- arch/arm64/kernel/pi/relocate.c | 62 ++++++++++++ arch/arm64/kernel/vmlinux.lds.S | 12 ++- 5 files changed, 82 insertions(+), 104 deletions(-)
arch/arm64/kernel/Makefile +2 -1
@@ -57,7 +57,8 @@ obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o obj-$(CONFIG_PARAVIRT) += paravirt.o -obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o pi/ +obj-$(CONFIG_RELOCATABLE) += pi/ +obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o obj-$(CONFIG_ELF_CORE) += elfcore.o obj-$(CONFIG_KEXEC_CORE) += machine_kexec.o relocate_kernel.o \
arch/arm64/kernel/head.S +7 -97
136 lines not on this page · raw message
arch/arm64/kernel/pi/Makefile +3 -2
@@ -38,5 +38,6 @@ $(obj)/lib-%.pi.o: OBJCOPYFLAGS += --prefix-alloc-sections=.init $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) -obj-y := kaslr_early.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o -extra-y := $(patsubst %.pi.o,%.o,$(obj-y)) +obj-y := relocate.pi.o +obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_early.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o +extra-y := $(patsubst %.pi.o,%.o,$(obj-y))
arch/arm64/kernel/pi/relocate.c new file +62 -0
62 lines not on this page · raw message
arch/arm64/kernel/vmlinux.lds.S +8 -4
@@ -270,15 +270,15 @@ SECTIONS HYPERVISOR_RELOC_SECTION .rela.dyn : ALIGN(8) { - __rela_start = .; + __pi_rela_start = .; *(.rela .rela*) - __rela_end = .; + __pi_rela_end = .; } .relr.dyn : ALIGN(8) { - __relr_start = .; + __pi_relr_start = .; *(.relr.dyn) - __relr_end = .; + __pi_relr_end = .; } . = ALIGN(SEGMENT_ALIGN);
@@ -317,6 +317,10 @@ SECTIONS init_pg_dir = .; . += INIT_DIR_SIZE; init_pg_end = .; +#ifdef CONFIG_RELOCATABLE + . += SZ_4K; /* stack for the early relocation code */ + early_init_stack = .; +#endif . = ALIGN(SEGMENT_ALIGN); __pecoff_data_size = ABSOLUTE(. - __initdata_begin);
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 11/50 arm64: idreg-override: Move to early mini C runtime
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
We will want to parse the ID register overrides even earlier, so that we can take them into account before creating the kernel mapping. So migrate the code and make it work in the context of the early C runtime. We will move the invocation to an earlier stage in a subsequent patch.
---
arch/arm64/kernel/Makefile | 4 +--
arch/arm64/kernel/head.S | 5 ++-
arch/arm64/kernel/image-vars.h | 9 +++++
arch/arm64/kernel/pi/Makefile | 5 +--
arch/arm64/kernel/{ => pi}/idreg-override.c | 35 +++++++++-----------
5 files changed, 30 insertions(+), 28 deletions(-)arch/arm64/kernel/Makefile +1 -3
@@ -33,8 +33,7 @@ obj-y := debug-monitors.o entry.o irq.o fpsimd.o \ return_address.o cpuinfo.o cpu_errata.o \ cpufeature.o alternative.o cacheinfo.o \ smp.o smp_spin_table.o topology.o smccc-call.o \ - syscall.o proton-pack.o idreg-override.o idle.o \ - patching.o + syscall.o proton-pack.o idle.o patching.o pi/ obj-$(CONFIG_COMPAT) += sys32.o signal32.o \ sys_compat.o
@@ -57,7 +56,6 @@ obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o obj-$(CONFIG_PARAVIRT) += paravirt.o -obj-$(CONFIG_RELOCATABLE) += pi/ obj-$(CONFIG_RANDOMIZE_BASE) += kaslr.o obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate-asm.o obj-$(CONFIG_ELF_CORE) += elfcore.o
arch/arm64/kernel/head.S +2 -3
@@ -510,10 +510,9 @@ SYM_FUNC_START_LOCAL(__primary_switched) #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) bl kasan_early_init #endif - mov x0, x21 // pass FDT address in x0 - bl early_fdt_map // Try mapping the FDT early mov x0, x20 // pass the full boot status - bl init_feature_override // Parse cpu feature overrides + mov x1, x22 // pass the low FDT mapping + bl __pi_init_feature_override // Parse cpu feature overrides #ifdef CONFIG_UNWIND_PATCH_PAC_INTO_SCS bl scs_patch_vmlinux #endif
arch/arm64/kernel/image-vars.h +9 -0
@@ -37,6 +37,15 @@ PROVIDE(__pi___memmove = __pi_memmove);PROVIDE (__pi___memset = __pi_memset);PROVIDE (__pi_vabits_actual = vabits_actual); +PROVIDE (__pi_id_aa64isar1_override = id_aa64isar1_override); +PROVIDE (__pi_id_aa64isar2_override = id_aa64isar2_override); +PROVIDE (__pi_id_aa64mmfr1_override = id_aa64mmfr1_override); +PROVIDE (__pi_id_aa64pfr0_override = id_aa64pfr0_override); +PROVIDE (__pi_id_aa64pfr1_override = id_aa64pfr1_override); +PROVIDE (__pi_id_aa64smfr0_override = id_aa64smfr0_override); +PROVIDE (__pi_id_aa64zfr0_override = id_aa64zfr0_override); +PROVIDE (__pi_arm64_sw_feature_override = arm64_sw_feature_override); +PROVIDE (__pi__ctype = _ctype);#ifdef CONFIG_KVM
arch/arm64/kernel/pi/Makefile +3 -2
@@ -38,6 +38,7 @@ $(obj)/lib-%.pi.o: OBJCOPYFLAGS += --prefix-alloc-sections=.init $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) -obj-y := relocate.pi.o -obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_early.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o +obj-y := idreg-override.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o +obj-$(CONFIG_RELOCATABLE) += relocate.pi.o +obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_early.pi.o extra-y := $(patsubst %.pi.o,%.o,$(obj-y))
arch/arm64/kernel/pi/idreg-override.c renamed from arch/arm64/kernel/idreg-override.c +15 -20
@@ -14,6 +14,8 @@#include <asm/cpufeature.h> #include <asm/setup.h> +#include "pi.h" +#define FTR_DESC_NAME_LEN 20#define FTR_DESC_FIELD_LEN 10#define FTR_ALIAS_NAME_LEN 30
@@ -21,15 +23,6 @@static u64 __boot_status__initdata ; -// temporary __prel64 related definitions -// to be removed when this code is moved under pi/ - -#define __prel64_initconst __initconst - -#define PREL64 (type, name) union { type *name; } - -#define prel64_pointer (__d) (__d) -typedef bool filter_t (u64 val);struct ftr_set_desc {
@@ -313,16 +306,11 @@ static __init void __parse_cmdline(const char *cmdline, bool parse_aliases) }while (1 ); } -static __init const u8 *get_bootargs_cmdline (void ) +static __init const u8 *get_bootargs_cmdline (const void *fdt) {const u8 *prop; -void *fdt;int node; - fdt =get_early_fdt_ptr (); -if (!fdt) -return NULL ; - node =fdt_path_offset (fdt,"/chosen" );if (node <0 )return NULL ;
@@ -334,9 +322,9 @@ static __init const u8 *get_bootargs_cmdline(void)return strlen (prop) ? prop :NULL ; } -static __init void parse_cmdline (void ) +static __init void parse_cmdline (const void *fdt) { -const u8 *prop =get_bootargs_cmdline (); +const u8 *prop =get_bootargs_cmdline (fdt);if (IS_ENABLED (CONFIG_CMDLINE_FORCE ) || !prop)__parse_cmdline (CONFIG_CMDLINE , true);
@@ -346,9 +334,9 @@ static __init void parse_cmdline(void) }/* Keep checkers quiet */ -void init_feature_override (u64 boot_status); +void init_feature_override (u64 boot_status,const void *fdt); -asmlinkage void __init init_feature_override(u64 boot_status) +asmlinkage void __init init_feature_override(u64 boot_status, const void *fdt) {struct arm64_ftr_override *override;const struct ftr_set_desc *reg;
@@ -364,7 +352,7 @@ asmlinkage void __init init_feature_override(u64 boot_status) __boot_status = boot_status; -parse_cmdline (); +parse_cmdline (fdt);for (i =0 ; i <ARRAY_SIZE (regs); i++) { reg =prel64_pointer (regs[i].reg);
@@ -373,3 +361,10 @@ asmlinkage void __init init_feature_override(u64 boot_status) (unsigned long)(override + 1)); } } + +char *__init skip_spaces (const char *str) +{ +while (isspace (*str)) + ++str; +return (char *)str; +}
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 12/50 arm64: kernel: Remove early fdt remap code
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The early FDT remap code is no longer used so let's drop it.
--- arch/arm64/include/asm/setup.h | 3 --- arch/arm64/kernel/setup.c | 15 --------------- 2 files changed, 18 deletions(-)
arch/arm64/include/asm/setup.h +0 -3
@@ -7,9 +7,6 @@#include <uapi/asm/setup.h> -void *get_early_fdt_ptr (void ); -void early_fdt_map (u64 dt_phys); -/* * These two variables are used in the head.S file. */
arch/arm64/kernel/setup.c +0 -15
@@ -166,21 +166,6 @@ static void __init smp_build_mpidr_hash(void)pr_warn ("Large number of MPIDR hash buckets detected\n" ); } -static void *early_fdt_ptr__initdata ; - -void __init *get_early_fdt_ptr (void ) -{ -return early_fdt_ptr; -} - -asmlinkage void __init early_fdt_map(u64 dt_phys) -{ -int fdt_size; - -early_fixmap_init (); - early_fdt_ptr =fixmap_remap_fdt (dt_phys, &fdt_size,PAGE_KERNEL ); -} -static void __init setup_machine_fdt (phys_addr_t dt_phys) {int size;
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 13/50 arm64: head: Clear BSS and the kernel page tables in one go
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
We will move the CPU feature overrides into BSS in a subsequent patch, and this requires that BSS is zeroed before the feature override detection code runs. So let's map BSS read-write in the ID map, and zero it via this mapping.
Since the kernel page tables are right next to it, and also zeroed via the ID map, let's drop the separate clear_page_tables() function, and just zero everything in one go.
--- arch/arm64/kernel/head.S | 33 +++++++------------- arch/arm64/kernel/vmlinux.lds.S | 3 ++ 2 files changed, 14 insertions(+), 22 deletions(-)
arch/arm64/kernel/head.S +11 -22
@@ -177,17 +177,6 @@ SYM_CODE_START_LOCAL(preserve_boot_args) ret SYM_CODE_END(preserve_boot_args) -SYM_FUNC_START_LOCAL(clear_page_tables) - /* - * Clear the init page tables. - */ - adrp x0, init_pg_dir - adrp x1, init_pg_end - sub x2, x1, x0 - mov x1, xzr - b __pi_memset // tail call -SYM_FUNC_END(clear_page_tables) - /* * Macro to populate page table entries, these entries can be pointers to the next level * or last level entries pointing to physical memory.
@@ -386,9 +375,9 @@ SYM_FUNC_START_LOCAL(create_idmap) map_memory x0, x1, x3, x6, x7, x3, IDMAP_PGD_ORDER, x10, x11, x12, x13, x14, EXTRA_SHIFT - /* Remap the kernel page tables r/w in the ID map */ + /* Remap BSS and the kernel page tables r/w in the ID map */ adrp x1, _text - adrp x2, init_pg_dir + adrp x2, __bss_start adrp x3, _end bic x4, x2, #SWAPPER_BLOCK_SIZE - 1 mov_q x5, SWAPPER_RW_MMUFLAGS
@@ -489,14 +478,6 @@ SYM_FUNC_START_LOCAL(__primary_switched) mov x0, x20 bl set_cpu_boot_mode_flag - // Clear BSS - adr_l x0, __bss_start - mov x1, xzr - adr_l x2, __bss_stop - sub x2, x2, x0 - bl __pi_memset - dsb ishst // Make zero page visible to PTW - #if VA_BITS > 48 adr_l x8, vabits_actual // Set this early so KASAN early init str x25, [x8] // ... observes the correct value
@@ -782,6 +763,15 @@ SYM_FUNC_START_LOCAL(__primary_switch) adrp x1, reserved_pg_dir adrp x2, init_idmap_pg_dir bl __enable_mmu + + // Clear BSS + adrp x0, __bss_start + mov x1, xzr + adrp x2, init_pg_end + sub x2, x2, x0 + bl __pi_memset + dsb ishst // Make zero page visible to PTW + #ifdef CONFIG_RELOCATABLE adrp x23, KERNEL_START and x23, x23, MIN_KIMG_ALIGN - 1
@@ -796,7 +786,6 @@ SYM_FUNC_START_LOCAL(__primary_switch) orr x23, x23, x0 // record kernel offset #endif #endif - bl clear_page_tables bl create_kernel_mapping adrp x1, init_pg_dir
arch/arm64/kernel/vmlinux.lds.S +3 -0
@@ -311,12 +311,15 @@ SECTIONS __pecoff_data_rawsize = ABSOLUTE(. - __initdata_begin); _edata = .; + /* start of zero-init region */ BSS_SECTION(SBSS_ALIGN, 0, 0) . = ALIGN(PAGE_SIZE); init_pg_dir = .; . += INIT_DIR_SIZE; init_pg_end = .; + /* end of zero-init region */ + #ifdef CONFIG_RELOCATABLE . += SZ_4K; /* stack for the early relocation code */ early_init_stack = .;
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 14/50 arm64: Move feature overrides into the BSS section
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
In order to allow the CPU feature override detection code to run even earlier, move the feature override global variables into BSS, which is the only part of the static kernel image that is mapped read-write in the initial ID map.
--- arch/arm64/kernel/cpufeature.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
arch/arm64/kernel/cpufeature.c +7 -7
@@ -655,13 +655,13 @@ static const struct arm64_ftr_bits ftr_raz[] = {#define ARM64_FTR_REG (id, table) \ __ARM64_FTR_REG_OVERRIDE(#id, id, table, &no_override) -struct arm64_ftr_override __ro_after_init id_aa64mmfr1_override; -struct arm64_ftr_override __ro_after_init id_aa64pfr0_override; -struct arm64_ftr_override __ro_after_init id_aa64pfr1_override; -struct arm64_ftr_override __ro_after_init id_aa64zfr0_override; -struct arm64_ftr_override __ro_after_init id_aa64smfr0_override; -struct arm64_ftr_override __ro_after_init id_aa64isar1_override; -struct arm64_ftr_override __ro_after_init id_aa64isar2_override; +struct arm64_ftr_override id_aa64mmfr1_override; +struct arm64_ftr_override id_aa64pfr0_override; +struct arm64_ftr_override id_aa64pfr1_override; +struct arm64_ftr_override id_aa64zfr0_override; +struct arm64_ftr_override id_aa64smfr0_override; +struct arm64_ftr_override id_aa64isar1_override; +struct arm64_ftr_override id_aa64isar2_override;struct arm64_ftr_override arm64_sw_feature_override;
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 15/50 arm64: head: Run feature override detection before mapping the kernel
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
To permit the feature overrides to be taken into account before the KASLR init code runs and the kernel mapping is created, move the detection code to an earlier stage in the boot.
In a subsequent patch, this will be taken advantage of by merging the preliminary and permanent mappings of the kernel text and data into a single one that gets created and relocated before start_kernel() is called.
--- arch/arm64/kernel/head.S | 17 +++++++++-------- arch/arm64/kernel/vmlinux.lds.S | 4 +--- 2 files changed, 10 insertions(+), 11 deletions(-)
arch/arm64/kernel/head.S +9 -8
@@ -375,9 +375,9 @@ SYM_FUNC_START_LOCAL(create_idmap) map_memory x0, x1, x3, x6, x7, x3, IDMAP_PGD_ORDER, x10, x11, x12, x13, x14, EXTRA_SHIFT - /* Remap BSS and the kernel page tables r/w in the ID map */ + /* Remap [.init].data, BSS and the kernel page tables r/w in the ID map */ adrp x1, _text - adrp x2, __bss_start + adrp x2, __initdata_begin adrp x3, _end bic x4, x2, #SWAPPER_BLOCK_SIZE - 1 mov_q x5, SWAPPER_RW_MMUFLAGS
@@ -491,9 +491,6 @@ SYM_FUNC_START_LOCAL(__primary_switched) #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) bl kasan_early_init #endif - mov x0, x20 // pass the full boot status - mov x1, x22 // pass the low FDT mapping - bl __pi_init_feature_override // Parse cpu feature overrides #ifdef CONFIG_UNWIND_PATCH_PAC_INTO_SCS bl scs_patch_vmlinux #endif
@@ -772,12 +769,16 @@ SYM_FUNC_START_LOCAL(__primary_switch) bl __pi_memset dsb ishst // Make zero page visible to PTW -#ifdef CONFIG_RELOCATABLE - adrp x23, KERNEL_START - and x23, x23, MIN_KIMG_ALIGN - 1 adrp x1, early_init_stack mov sp, x1 mov x29, xzr + mov x0, x20 // pass the full boot status + mov x1, x22 // pass the low FDT mapping + bl __pi_init_feature_override // Parse cpu feature overrides + +#ifdef CONFIG_RELOCATABLE + adrp x23, KERNEL_START + and x23, x23, MIN_KIMG_ALIGN - 1 #ifdef CONFIG_RANDOMIZE_BASE mov x0, x22 bl __pi_kaslr_early_init
arch/arm64/kernel/vmlinux.lds.S +1 -3
@@ -320,10 +320,8 @@ SECTIONS init_pg_end = .; /* end of zero-init region */ -#ifdef CONFIG_RELOCATABLE - . += SZ_4K; /* stack for the early relocation code */ + . += SZ_4K; /* stack for the early C runtime */ early_init_stack = .; -#endif . = ALIGN(SEGMENT_ALIGN); __pecoff_data_size = ABSOLUTE(. - __initdata_begin);
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 16/50 arm64: head: move dynamic shadow call stack patching into early C runtime
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Once we update the early kernel mapping code to only map the kernel once with the right permissions, we can no longer perform code patching via this mapping.
So move this code to an earlier stage of the boot, right after applying the relocations.
---
arch/arm64/include/asm/scs.h | 4 +--
arch/arm64/kernel/Makefile | 8 ------
arch/arm64/kernel/head.S | 8 +++---
arch/arm64/kernel/module.c | 2 +-
arch/arm64/kernel/pi/Makefile | 10 +++++---
arch/arm64/kernel/{ => pi}/patch-scs.c | 26 ++++++++++----------
6 files changed, 27 insertions(+), 31 deletions(-)arch/arm64/include/asm/scs.h +2 -2
@@ -72,8 +72,8 @@ static inline void dynamic_scs_init(void)static inline void dynamic_scs_init (void ) {}#endif -int scs_patch (const u8 eh_frame[],int size); -asmlinkage void scs_patch_vmlinux(void); +int __pi_scs_patch (const u8 eh_frame[],int size); +asmlinkage void __pi_scs_patch_vmlinux(void);#endif /* __ASSEMBLY __ */
arch/arm64/kernel/Makefile +0 -8
@@ -71,14 +71,6 @@ obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o obj-$(CONFIG_ARM64_MTE) += mte.o obj-y += vdso-wrap.o obj-$(CONFIG_COMPAT_VDSO) += vdso32-wrap.o -obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.o - -# We need to prevent the SCS patching code from patching itself. Using -# -mbranch-protection=none here to avoid the patchable PAC opcodes from being -# generated triggers an issue with full LTO on Clang, which stops emitting PAC -# instructions altogether. So instead, omit the unwind tables used by the -# patching code, so it will not be able to locate its own PAC instructions. -CFLAGS_patch-scs.o += -fno-asynchronous-unwind-tables -fno-unwind-tables # Force dependency (vdso*-wrap.S includes vdso.so through incbin) $(obj)/vdso-wrap.o: $(obj)/vdso/vdso.so
arch/arm64/kernel/head.S +5 -3
@@ -490,9 +490,6 @@ SYM_FUNC_START_LOCAL(__primary_switched) #endif #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) bl kasan_early_init -#endif -#ifdef CONFIG_UNWIND_PATCH_PAC_INTO_SCS - bl scs_patch_vmlinux #endif mov x0, x20 bl finalise_el2 // Prefer VHE if possible
@@ -794,6 +791,11 @@ SYM_FUNC_START_LOCAL(__primary_switch) #ifdef CONFIG_RELOCATABLE mov x0, x23 bl __pi_relocate_kernel +#endif +#ifdef CONFIG_UNWIND_PATCH_PAC_INTO_SCS + ldr x0, =__eh_frame_start + ldr x1, =__eh_frame_end + bl __pi_scs_patch_vmlinux #endif ldr x8, =__primary_switched adrp x0, KERNEL_START // __pa(KERNEL_START)
arch/arm64/kernel/module.c +1 -1
@@ -595,7 +595,7 @@ int module_finalize(const Elf_Ehdr *hdr,if (scs_is_dynamic ()) { s =find_section (hdr, sechdrs,".init.eh_frame" );if (s) -scs_patch ((void *)s->sh_addr, s->sh_size); +__pi_scs_patch ((void *)s->sh_addr, s->sh_size); }return module_init_ftrace_plt (hdr, sechdrs, me);
arch/arm64/kernel/pi/Makefile +6 -4
@@ -38,7 +38,9 @@ $(obj)/lib-%.pi.o: OBJCOPYFLAGS += --prefix-alloc-sections=.init $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) -obj-y := idreg-override.pi.o lib-fdt.pi.o lib-fdt_ro.pi.o -obj-$(CONFIG_RELOCATABLE) += relocate.pi.o -obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_early.pi.o -extra-y := $(patsubst %.pi.o,%.o,$(obj-y)) +obj-y := idreg-override.pi.o \ + lib-fdt.pi.o lib-fdt_ro.pi.o +obj-$(CONFIG_RELOCATABLE) += relocate.pi.o +obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_early.pi.o +obj-$(CONFIG_UNWIND_PATCH_PAC_INTO_SCS) += patch-scs.pi.o +extra-y := $(patsubst %.pi.o,%.o,$(obj-y))
arch/arm64/kernel/pi/patch-scs.c renamed from arch/arm64/kernel/patch-scs.c +13 -13
@@ -4,14 +4,11 @@* Author: Ard Biesheuvel <[email protected]> */ -#include <linux/bug.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/linkage.h> -#include <linux/printk.h> #include <linux/types.h> -#include <asm/cacheflush.h> #include <asm/scs.h> //
@@ -81,7 +78,11 @@ static void __always_inline scs_patch_loc(u64 loc)*/ return ; } -dcache_clean_pou (loc, loc +sizeof (u32)); +if (IS_ENABLED (CONFIG_ARM64_WORKAROUND_CLEAN_CACHE )) + asm("dc civac, %0" ::"r" (loc)); +else + asm(ALTERNATIVE("dc cvau, %0", "nop", ARM64_HAS_CACHE_IDC) + :: "r"(loc)); }/*
@@ -128,10 +129,10 @@ struct eh_frame { }; }; -static int noinstr scs_handle_fde_frame(const struct eh_frame *frame, -bool fde_has_augmentation_data, -int code_alignment_factor, -bool dry_run) +static int scs_handle_fde_frame (const struct eh_frame *frame, +bool fde_has_augmentation_data, +int code_alignment_factor, +bool dry_run) {int size = frame->size - offsetof(struct eh_frame , opcodes) +4 ;u64 loc = (u64 )offset_to_ptr (&frame->initial_loc);
@@ -198,14 +199,13 @@ static int noinstr scs_handle_fde_frame(const struct eh_frame *frame,break ;default : -pr_err ("unhandled opcode: %02x in FDE frame %lx\n" , opcode[-1 ], (uintptr_t )frame);return -ENOEXEC ; } }return 0 ; } -int noinstr scs_patch(const u8 eh_frame[], int size) +int scs_patch (const u8 eh_frame[],int size) {const u8 *p = eh_frame;
@@ -251,12 +251,12 @@ int noinstr scs_patch(const u8 eh_frame[], int size)return 0 ; } -asmlinkage void __init scs_patch_vmlinux(void) +asmlinkage void __init scs_patch_vmlinux(const u8 start[], const u8 end[]) {if (!should_patch_pac_into_scs ())return ; -WARN_ON (scs_patch (__eh_frame_start, __eh_frame_end - __eh_frame_start)); -icache_inval_all_pou (); +scs_patch (start, end - start); + asm("ic ialluis" );isb (); }
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 17/50 arm64: cpufeature: Add helper to test for CPU feature overrides
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add some helpers to extract and apply feature overrides to the bare idreg values. This involves inspecting the value and mask of the specific field that we are interested in, given that an override value/mask pair might be invalid for one field but valid for another.
Then, wire up the new helper for the hVHE test - note that we can drop the sysreg test here, as the override will be invalid when trying to enable hVHE on non-VHE capable hardware.
--- arch/arm64/include/asm/cpufeature.h | 39 ++++++++++++++++++++ arch/arm64/kernel/cpufeature.c | 9 +---- 2 files changed, 40 insertions(+), 8 deletions(-)
arch/arm64/include/asm/cpufeature.h +39 -0
@@ -915,6 +915,45 @@ extern struct arm64_ftr_override id_aa64isar2_override;extern struct arm64_ftr_override arm64_sw_feature_override; +static inline +u64 arm64_apply_feature_override (u64 val,int feat,int width, +const struct arm64_ftr_override *override) +{ +u64 oval = override->val; + +/* +* When it encounters an invalid override (e.g., an override that +* cannot be honoured due to a missing CPU feature), the early idreg +* override code will set the mask to 0x0 and the value to non-zero for +* the field in question. In order to determine whether the override is +* valid or not for the field we are interested in, we first need to +* disregard bits belonging to other fields. +*/ + oval &=GENMASK_ULL (feat + width -1 , feat); + +/* +* The override is valid if all value bits are accounted for in the +* mask. If so, replace the masked bits with the override value. +*/ +if (oval == (oval & override->mask)) { + val &= ~override->mask; + val |= oval; + } + +/* Extract the field from the updated value */ +return cpuid_feature_extract_unsigned_field (val, feat); +} + +static inline bool arm64_test_sw_feature_override (int feat) +{ +/* +* Software features are pseudo CPU features that have no underlying +* CPUID system register value to apply the override to. +*/ +return arm64_apply_feature_override (0 , feat,4 , + &arm64_sw_feature_override); +} +u32 get_kvm_ipa_limit (void );void dump_cpu_features (void );
arch/arm64/kernel/cpufeature.c +1 -8
@@ -2042,14 +2042,7 @@ static bool has_nested_virt_support(const struct arm64_cpu_capabilities *cap,static bool hvhe_possible (const struct arm64_cpu_capabilities *entry,int __unused) { -u64 val; - - val =read_sysreg (id_aa64mmfr1_el1); -if (!cpuid_feature_extract_unsigned_field (val,ID_AA64MMFR1_EL1_VH_SHIFT )) -return false; - - val = arm64_sw_feature_override.val & arm64_sw_feature_override.mask; -return cpuid_feature_extract_unsigned_field (val,ARM64_SW_FEATURE_OVERRIDE_HVHE ); +return arm64_test_sw_feature_override (ARM64_SW_FEATURE_OVERRIDE_HVHE ); }#ifdef CONFIG_ARM64_PAN
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 18/50 arm64: kaslr: Use feature override instead of parsing the cmdline again
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The early kaslr code open codes the detection of 'nokaslr' on the kernel command line, and this is no longer necessary now that the feature detection code, which also looks for the same string, executes before this code.
--- arch/arm64/include/asm/cpufeature.h | 5 ++ arch/arm64/kernel/kaslr.c | 4 +- arch/arm64/kernel/pi/kaslr_early.c | 53 +------------------- 3 files changed, 7 insertions(+), 55 deletions(-)
arch/arm64/include/asm/cpufeature.h +5 -0
@@ -954,6 +954,11 @@ static inline bool arm64_test_sw_feature_override(int feat) &arm64_sw_feature_override); } +static inline bool kaslr_disabled_cmdline (void ) +{ +return arm64_test_sw_feature_override (ARM64_SW_FEATURE_OVERRIDE_NOKASLR ); +} +u32 get_kvm_ipa_limit (void );void dump_cpu_features (void );
arch/arm64/kernel/kaslr.c +1 -3
@@ -16,9 +16,7 @@ bool __ro_after_init __kaslr_is_enabled = false;void __init kaslr_init (void ) { -if (cpuid_feature_extract_unsigned_field (arm64_sw_feature_override.val & - arm64_sw_feature_override.mask, -ARM64_SW_FEATURE_OVERRIDE_NOKASLR )) { +if (kaslr_disabled_cmdline ()) {pr_info ("KASLR disabled on command line\n" );return ; }
arch/arm64/kernel/pi/kaslr_early.c +1 -52
65 lines not on this page · raw message
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 19/50 arm64: idreg-override: Create a pseudo feature for rodata=off
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add rodata=off to the set of kernel command line options that is parsed early using the CPU feature override detection code, so we can easily refer to it when creating the kernel mapping.
--- arch/arm64/include/asm/cpufeature.h | 1 + arch/arm64/kernel/pi/idreg-override.c | 2 ++ 2 files changed, 3 insertions(+)
arch/arm64/include/asm/cpufeature.h +1 -0
@@ -17,6 +17,7 @@#define ARM64_SW_FEATURE_OVERRIDE_NOKASLR 0#define ARM64_SW_FEATURE_OVERRIDE_HVHE 4 +#define ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF 8#ifndef __ASSEMBLY__
arch/arm64/kernel/pi/idreg-override.c +2 -0
@@ -159,6 +159,7 @@ static const struct ftr_set_desc sw_features __prel64_initconst = { .fields = {FIELD ("nokaslr" ,ARM64_SW_FEATURE_OVERRIDE_NOKASLR ,NULL ),FIELD ("hvhe" ,ARM64_SW_FEATURE_OVERRIDE_HVHE , hvhe_filter), + FIELD("rodataoff", ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF, NULL), {} }, };
@@ -190,6 +191,7 @@ static const struct { { "arm64.nomops", "id_aa64isar2.mops=0" }, { "arm64.nomte", "id_aa64pfr1.mte=0" }, { "nokaslr", "arm64_sw.nokaslr=1" }, + { "rodata=off", "arm64_sw.rodataoff=1" }, };static int __init parse_hexdigit (const char *p,u64 *v)
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 20/50 arm64: Add helpers to probe local CPU for PAC and BTI support
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add some helpers that will be used by the early kernel mapping code to check feature support on the local CPU. This permits the early kernel mapping to be created with the right attributes, removing the need for tearing it down and recreating it.
--- arch/arm64/include/asm/cpufeature.h | 32 ++++++++++++++++++++ 1 file changed, 32 insertions(+)
arch/arm64/include/asm/cpufeature.h +32 -0
@@ -963,6 +963,38 @@ static inline bool kaslr_disabled_cmdline(void)u32 get_kvm_ipa_limit (void );void dump_cpu_features (void ); +static inline bool cpu_has_bti (void ) +{ +if (!IS_ENABLED (CONFIG_ARM64_BTI )) +return false; + +return arm64_apply_feature_override (read_cpuid (ID_AA64PFR1_EL1 ), +ID_AA64PFR1_EL1_BT_SHIFT ,4 , + &id_aa64pfr1_override); +} + +static inline bool cpu_has_pac (void ) +{ +u64 isar1, isar2; + +if (!IS_ENABLED (CONFIG_ARM64_PTR_AUTH )) +return false; + + isar1 =read_cpuid (ID_AA64ISAR1_EL1 ); + isar2 =read_cpuid (ID_AA64ISAR2_EL1 ); + +if (arm64_apply_feature_override (isar1,ID_AA64ISAR1_EL1_APA_SHIFT ,4 , + &id_aa64isar1_override)) +return true; + +if (arm64_apply_feature_override (isar1,ID_AA64ISAR1_EL1_API_SHIFT ,4 , + &id_aa64isar1_override)) +return true; + +return arm64_apply_feature_override (isar2,ID_AA64ISAR2_EL1_APA3_SHIFT ,4 , + &id_aa64isar2_override); +} +#endif /* __ASSEMBLY__ */ #endif
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 21/50 arm64: head: allocate more pages for the kernel mapping
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
In preparation for switching to an early kernel mapping routine that maps each segment according to its precise boundaries, and with the correct attributes, let's allocate some extra pages for page tables for the 4k page size configuration. This is necessary because the start and end of each segment may not be aligned to the block size, and so we'll need an extra page table at each segment boundary.
--- arch/arm64/include/asm/kernel-pgtable.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-)
arch/arm64/include/asm/kernel-pgtable.h +10 -1
@@ -68,7 +68,7 @@ +EARLY_PGDS ((vstart), (vend), add)/* each PGDIR needs a next level page table */ \ +EARLY_PUDS ((vstart), (vend), add)/* each PUD needs a next level page table */ \ + EARLY_PMDS((vstart), (vend), add)) /* each PMD needs a next level page table */ -#define INIT_DIR_SIZE (PAGE_SIZE * EARLY_PAGES(KIMAGE_VADDR, _end, EXTRA_PAGE)) +#define INIT_DIR_SIZE (PAGE_SIZE * (EARLY_PAGES(KIMAGE_VADDR, _end, EXTRA_PAGE) + EARLY_SEGMENT_EXTRA_PAGES)) /* the initial ID map may need two extra pages if it needs to be extended */#if VA_BITS <48
@@ -89,6 +89,15 @@#define SWAPPER_TABLE_SHIFT PMD_SHIFT#endif +/* The number of segments in the kernel image (text, rodata, inittext, initdata, data+bss) */ +#define KERNEL_SEGMENT_COUNT 5 + +#if SWAPPER_BLOCK_SIZE >SEGMENT_ALIGN +#define EARLY_SEGMENT_EXTRA_PAGES (KERNEL_SEGMENT_COUNT + 1) +#else +#define EARLY_SEGMENT_EXTRA_PAGES 0 +#endif +/* * Initial memory map attributes. */
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 22/50 arm64: head: move memstart_offset_seed handling to C code
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Now that we can set BSS variables from the early code running from the ID map, we can set memstart_offset_seed directly from the C code that derives the value instead of passing it back and forth between C and asm code.
--- arch/arm64/kernel/head.S | 7 ------- arch/arm64/kernel/image-vars.h | 1 + arch/arm64/kernel/pi/kaslr_early.c | 4 ++++ 3 files changed, 5 insertions(+), 7 deletions(-)
arch/arm64/kernel/head.S +0 -7
@@ -82,7 +82,6 @@ * x21 primary_entry() .. start_kernel() FDT pointer passed at boot in x0 * x22 create_idmap() .. start_kernel() ID map VA of the DT blob * x23 __primary_switch() physical misalignment/KASLR offset - * x24 __primary_switch() linear map KASLR seed * x25 primary_entry() .. start_kernel() supported VA size * x28 create_idmap() callee preserved temp register */
@@ -483,11 +482,6 @@ SYM_FUNC_START_LOCAL(__primary_switched) str x25, [x8] // ... observes the correct value dc civac, x8 // Make visible to booting secondaries #endif - -#ifdef CONFIG_RANDOMIZE_BASE - adrp x5, memstart_offset_seed // Save KASLR linear map seed - strh w24, [x5, :lo12:memstart_offset_seed] -#endif #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) bl kasan_early_init #endif
@@ -779,7 +773,6 @@ SYM_FUNC_START_LOCAL(__primary_switch) #ifdef CONFIG_RANDOMIZE_BASE mov x0, x22 bl __pi_kaslr_early_init - and x24, x0, #SZ_2M - 1 // capture memstart offset seed bic x0, x0, #SZ_2M - 1 orr x23, x23, x0 // record kernel offset #endif
arch/arm64/kernel/image-vars.h +1 -0
@@ -46,6 +46,7 @@ PROVIDE(__pi_id_aa64smfr0_override = id_aa64smfr0_override);PROVIDE (__pi_id_aa64zfr0_override = id_aa64zfr0_override);PROVIDE (__pi_arm64_sw_feature_override = arm64_sw_feature_override);PROVIDE (__pi__ctype = _ctype); +PROVIDE (__pi_memstart_offset_seed = memstart_offset_seed);#ifdef CONFIG_KVM
arch/arm64/kernel/pi/kaslr_early.c +4 -0
@@ -16,6 +16,8 @@#include <asm/memory.h> #include <asm/pgtable.h> +extern u16 memstart_offset_seed; +static u64 __init get_kaslr_seed (void *fdt) {static char const chosen_str[]__initconst ="chosen" ;
@@ -51,6 +53,8 @@ asmlinkage u64 __init kaslr_early_init(void *fdt)return 0 ; } + memstart_offset_seed = seed &U16_MAX ; +/* * OK, so we are proceeding with KASLR enabled. Calculate a suitable * kernel image offset from the seed. Let's place the kernel in the
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 23/50 arm64: mm: Make kaslr_requires_kpti() a static inline
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
In preparation for moving the first assignment of arm64_use_ng_mappings to an earlier stage in the boot, ensure that kaslr_requires_kpti() is accessible without relying on the core kernel's view on whether or not KASLR is enabled. So make it a static inline, and move the kaslr_enabled() check out of it and into the callers, one of which will disappear in a subsequent patch.
Once/when support for the obsolete ThunderX 1 platform is dropped, this check reduces to a E0PD feature check on the local CPU.
--- arch/arm64/include/asm/mmu.h | 38 +++++++++++++++++- arch/arm64/kernel/cpufeature.c | 42 +------------------- arch/arm64/kernel/setup.c | 2 +- 3 files changed, 39 insertions(+), 43 deletions(-)
arch/arm64/include/asm/mmu.h +37 -1
@@ -71,7 +71,43 @@ extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, pgprot_t prot, bool page_mappings_only);extern void *fixmap_remap_fdt (phys_addr_t dt_phys,int *size,pgprot_t prot);extern void mark_linear_text_alias_ro (void ); -extern bool kaslr_requires_kpti (void ); + +/* +* This check is triggered during the early boot before the cpufeature +* is initialised. Checking the status on the local CPU allows the boot +* CPU to detect the need for non-global mappings and thus avoiding a +* pagetable re-write after all the CPUs are booted. This check will be +* anyway run on individual CPUs, allowing us to get the consistent +* state once the SMP CPUs are up and thus make the switch to non-global +* mappings if required. +*/ +static inline bool kaslr_requires_kpti (void ) +{ +/* +* E0PD does a similar job to KPTI so can be used instead +* where available. +*/ +if (IS_ENABLED (CONFIG_ARM64_E0PD )) { +u64 mmfr2 =read_sysreg_s (SYS_ID_AA64MMFR2_EL1 ); +if (cpuid_feature_extract_unsigned_field (mmfr2, +ID_AA64MMFR2_EL1_E0PD_SHIFT )) +return false; + } + +/* +* Systems affected by Cavium erratum 24756 are incompatible +* with KPTI. +*/ +if (IS_ENABLED (CONFIG_CAVIUM_ERRATUM_27456 )) { +extern const struct midr_range cavium_erratum_27456_cpus[]; + +if (is_midr_in_range_list (read_cpuid_id (), + cavium_erratum_27456_cpus)) +return false; + } + +return true; +}#define INIT_MM_CONTEXT (name) \ .pgd = init_pg_dir,
arch/arm64/kernel/cpufeature.c +1 -41
54 lines not on this page · raw message
arch/arm64/kernel/setup.c +1 -1
@@ -288,7 +288,7 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)* mappings from the start, avoiding the cost of rewriting * everything later. */ - arm64_use_ng_mappings =kaslr_requires_kpti (); + arm64_use_ng_mappings =kaslr_enabled () &&kaslr_requires_kpti ();early_fixmap_init ();early_ioremap_init ();
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 24/50 arm64: mmu: Make __cpu_replace_ttbr1() out of line
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
__cpu_replace_ttbr1() is a static inline, and so it gets instantiated wherever it is used. This is not really necessary, as it is never called on a hot path. It also has the unfortunate side effect that the symbol idmap_cpu_replace_ttbr1 may never be referenced from kCFI enabled C code, and this means the type id symbol may not exist either. This will result in a build error once we start referring to this symbol from asm code as well. (Note that this problem only occurs when CnP, KAsan and suspend/resume are all disabled in the Kconfig but that is a valid config, if unusual).
So let's just move it out of line so all callers will share the same implementation, which will reference idmap_cpu_replace_ttbr1 unconditionally.
--- arch/arm64/include/asm/mmu_context.h | 32 +------------------- arch/arm64/mm/mmu.c | 32 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 31 deletions(-)
arch/arm64/include/asm/mmu_context.h +1 -31
@@ -148,37 +148,7 @@ static inline void cpu_install_ttbr0(phys_addr_t ttbr0, unsigned long t0sz)isb (); } -/* -* Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD, -* avoiding the possibility of conflicting TLB entries being allocated. -*/ -static inline void __cpu_replace_ttbr1 (pgd_t *pgdp,pgd_t *idmap,bool cnp) -{ -typedef void (ttbr_replace_func )(phys_addr_t ); -extern ttbr_replace_func idmap_cpu_replace_ttbr1; -ttbr_replace_func *replace_phys; -unsigned long daif; - -/* phys_to_ttbr() zeros lower 2 bits of ttbr with 52-bit PA */ -phys_addr_t ttbr1 =phys_to_ttbr (virt_to_phys (pgdp)); - -if (cnp) - ttbr1 |=TTBR_CNP_BIT ; - - replace_phys = (void *)__pa_symbol (idmap_cpu_replace_ttbr1); - -__cpu_install_idmap (idmap); - -/* -* We really don't want to take *any* exceptions while TTBR1 is -* in the process of being replaced so mask everything. -*/ - daif =local_daif_save (); -replace_phys (ttbr1); -local_daif_restore (daif); - -cpu_uninstall_idmap (); -} +void __cpu_replace_ttbr1 (pgd_t *pgdp,pgd_t *idmap,bool cnp);static inline void cpu_enable_swapper_cnp (void ) {
arch/arm64/mm/mmu.c +32 -0
@@ -1486,3 +1486,35 @@ void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte {set_pte_at (vma->vm_mm, addr, ptep, pte); } + +/* +* Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD, +* avoiding the possibility of conflicting TLB entries being allocated. +*/ +void __cpu_replace_ttbr1 (pgd_t *pgdp,pgd_t *idmap,bool cnp) +{ +typedef void (ttbr_replace_func )(phys_addr_t ); +extern ttbr_replace_func idmap_cpu_replace_ttbr1; +ttbr_replace_func *replace_phys; +unsigned long daif; + +/* phys_to_ttbr() zeros lower 2 bits of ttbr with 52-bit PA */ +phys_addr_t ttbr1 =phys_to_ttbr (virt_to_phys (pgdp)); + +if (cnp) + ttbr1 |=TTBR_CNP_BIT ; + + replace_phys = (void *)__pa_symbol (idmap_cpu_replace_ttbr1); + +__cpu_install_idmap (idmap); + +/* +* We really don't want to take *any* exceptions while TTBR1 is +* in the process of being replaced so mask everything. +*/ + daif =local_daif_save (); +replace_phys (ttbr1); +local_daif_restore (daif); + +cpu_uninstall_idmap (); +}
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 25/50 arm64: head: Move early kernel mapping routines into C code
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The asm version of the kernel mapping code works fine for creating a coarse grained identity map, but for mapping the kernel down to its exact boundaries with the right attributes, it is not suitable. This is why we create a preliminary RWX kernel mapping first, and then rebuild it from scratch later on.
So let's reimplement this in C, in a way that will make it unnecessary to create the kernel page tables yet another time in paging_init().
--- arch/arm64/include/asm/archrandom.h | 2 - arch/arm64/include/asm/scs.h | 32 +--- arch/arm64/kernel/head.S | 52 +------ arch/arm64/kernel/image-vars.h | 19 +++ arch/arm64/kernel/pi/Makefile | 1 + arch/arm64/kernel/pi/idreg-override.c | 22 ++- arch/arm64/kernel/pi/kaslr_early.c | 12 +- arch/arm64/kernel/pi/map_kernel.c | 164 ++++++++++++++++++++ arch/arm64/kernel/pi/map_range.c | 88 +++++++++++ arch/arm64/kernel/pi/patch-scs.c | 16 +- arch/arm64/kernel/pi/pi.h | 14 ++ arch/arm64/kernel/pi/relocate.c | 2 + arch/arm64/kernel/setup.c | 7 - arch/arm64/kernel/vmlinux.lds.S | 4 +- arch/arm64/mm/proc.S | 1 + 15 files changed, 315 insertions(+), 121 deletions(-)
arch/arm64/include/asm/archrandom.h +0 -2
@@ -129,6 +129,4 @@ static inline bool __init __early_cpu_has_rndr(void) return (ftr >> ID_AA64ISAR0_EL1_RNDR_SHIFT) & 0xf; } -u64 kaslr_early_init (void *fdt); -#endif /* _ASM_ARCHRANDOM_H */
arch/arm64/include/asm/scs.h +3 -29
@@ -33,37 +33,11 @@#include <asm/cpufeature.h> #ifdef CONFIG_UNWIND_PATCH_PAC_INTO_SCS -static inline bool should_patch_pac_into_scs (void ) -{ -u64 reg; - -/* -* We only enable the shadow call stack dynamically if we are running -* on a system that does not implement PAC or BTI. PAC and SCS provide -* roughly the same level of protection, and BTI relies on the PACIASP -* instructions serving as landing pads, preventing us from patching -* those instructions into something else. -*/ - reg =read_sysreg_s (SYS_ID_AA64ISAR1_EL1 ); -if (SYS_FIELD_GET (ID_AA64ISAR1_EL1 ,APA , reg) | -SYS_FIELD_GET (ID_AA64ISAR1_EL1 ,API , reg)) -return false; - - reg =read_sysreg_s (SYS_ID_AA64ISAR2_EL1 ); -if (SYS_FIELD_GET (ID_AA64ISAR2_EL1 ,APA3 , reg)) -return false; - -if (IS_ENABLED (CONFIG_ARM64_BTI_KERNEL )) { - reg =read_sysreg_s (SYS_ID_AA64PFR1_EL1 ); -if (reg & (0xf <<ID_AA64PFR1_EL1_BT_SHIFT )) -return false; - } -return true; -} -static inline void dynamic_scs_init (void ) { -if (should_patch_pac_into_scs ()) { +extern bool __pi_dynamic_scs_is_enabled; + +if (__pi_dynamic_scs_is_enabled) {pr_info ("Enabling dynamic shadow call stack\n" );static_branch_enable (&dynamic_scs_enabled); }
arch/arm64/kernel/head.S +1 -51
76 lines not on this page · raw message
arch/arm64/kernel/image-vars.h +19 -0
@@ -45,9 +45,28 @@ PROVIDE(__pi_id_aa64pfr1_override = id_aa64pfr1_override);PROVIDE (__pi_id_aa64smfr0_override = id_aa64smfr0_override);PROVIDE (__pi_id_aa64zfr0_override = id_aa64zfr0_override);PROVIDE (__pi_arm64_sw_feature_override = arm64_sw_feature_override); +PROVIDE (__pi_arm64_use_ng_mappings = arm64_use_ng_mappings); +#ifdef CONFIG_CAVIUM_ERRATUM_27456 +PROVIDE (__pi_cavium_erratum_27456_cpus = cavium_erratum_27456_cpus); +#endif PROVIDE (__pi__ctype = _ctype);PROVIDE (__pi_memstart_offset_seed = memstart_offset_seed); +PROVIDE (__pi_init_pg_dir = init_pg_dir); +PROVIDE (__pi_init_pg_end = init_pg_end); + +PROVIDE (__pi__text = _text); +PROVIDE (__pi__stext = _stext); +PROVIDE (__pi__etext = _etext); +PROVIDE (__pi___start_rodata = __start_rodata); +PROVIDE (__pi___inittext_begin = __inittext_begin); +PROVIDE (__pi___inittext_end = __inittext_end); +PROVIDE (__pi___initdata_begin = __initdata_begin); +PROVIDE (__pi___initdata_end = __initdata_end); +PROVIDE (__pi__data = _data); +PROVIDE (__pi___bss_start = __bss_start); +PROVIDE (__pi__end = _end); +#ifdef CONFIG_KVM /*
arch/arm64/kernel/pi/Makefile +1 -0
@@ -39,6 +39,7 @@ $(obj)/lib-%.o: $(srctree)/lib/%.c FORCE $(call if_changed_rule,cc_o_c) obj-y := idreg-override.pi.o \ + map_kernel.pi.o map_range.pi.o \ lib-fdt.pi.o lib-fdt_ro.pi.o obj-$(CONFIG_RELOCATABLE) += relocate.pi.o obj-$(CONFIG_RANDOMIZE_BASE) += kaslr_early.pi.o
arch/arm64/kernel/pi/idreg-override.c +10 -12
@@ -308,37 +308,35 @@ static __init void __parse_cmdline(const char *cmdline, bool parse_aliases) }while (1 ); } -static __init const u8 *get_bootargs_cmdline (const void *fdt) +static __init const u8 *get_bootargs_cmdline (const void *fdt,int node) { +static char const bootargs[]__initconst ="bootargs" ;const u8 *prop; -int node; - node =fdt_path_offset (fdt,"/chosen" );if (node <0 )return NULL ; - prop =fdt_getprop (fdt, node,"bootargs" ,NULL ); + prop =fdt_getprop (fdt, node, bootargs,NULL );if (!prop)return NULL ;return strlen (prop) ? prop :NULL ; } -static __init void parse_cmdline (const void *fdt) +static __init void parse_cmdline (const void *fdt,int chosen) { -const u8 *prop =get_bootargs_cmdline (fdt); +static char const cmdline[]__initconst =CONFIG_CMDLINE ; +const u8 *prop =get_bootargs_cmdline (fdt, chosen);if (IS_ENABLED (CONFIG_CMDLINE_FORCE ) || !prop) -__parse_cmdline (CONFIG_CMDLINE , true); +__parse_cmdline (cmdline, true);if (!IS_ENABLED (CONFIG_CMDLINE_FORCE ) && prop)__parse_cmdline (prop, true); } -/* Keep checkers quiet */ -void init_feature_override (u64 boot_status,const void *fdt); - -asmlinkage void __init init_feature_override(u64 boot_status, const void *fdt) +void __init init_feature_override (u64 boot_status,const void *fdt, +int chosen) {struct arm64_ftr_override *override;const struct ftr_set_desc *reg;
@@ -354,7 +352,7 @@ asmlinkage void __init init_feature_override(u64 boot_status, const void *fdt) __boot_status = boot_status; -parse_cmdline (fdt); +parse_cmdline (fdt, chosen);for (i =0 ; i <ARRAY_SIZE (regs); i++) { reg =prel64_pointer (regs[i].reg);
arch/arm64/kernel/pi/kaslr_early.c +6 -6
@@ -16,17 +16,17 @@#include <asm/memory.h> #include <asm/pgtable.h> +#include "pi.h" +extern u16 memstart_offset_seed; -static u64 __init get_kaslr_seed (void *fdt) +static u64 __init get_kaslr_seed (void *fdt,int node) { -static char const chosen_str[]__initconst ="chosen" ;static char const seed_str[]__initconst ="kaslr-seed" ; -int node, len;fdt64_t *prop;u64 ret; +int len; - node =fdt_path_offset (fdt, chosen_str);if (node <0 )return 0 ;
@@ -39,14 +39,14 @@ static u64 __init get_kaslr_seed(void *fdt)return ret; } -asmlinkage u64 __init kaslr_early_init(void *fdt) +u64 __init kaslr_early_init (void *fdt,int chosen) {u64 seed, range;if (kaslr_disabled_cmdline ())return 0 ; - seed =get_kaslr_seed (fdt); + seed =get_kaslr_seed (fdt, chosen);if (!seed) {if (!__early_cpu_has_rndr () || !__arm64_rndr ((unsigned long *)&seed))
arch/arm64/kernel/pi/map_kernel.c new file +164 -0
164 lines not on this page · raw message
arch/arm64/kernel/pi/map_range.c new file +88 -0
88 lines not on this page · raw message
arch/arm64/kernel/pi/patch-scs.c +4 -12
@@ -11,6 +11,10 @@#include <asm/scs.h> +#include "pi.h" + +bool dynamic_scs_is_enabled; +// // This minimal DWARF CFI parser is partially based on the code in // arch/arc/kernel/unwind.c, and on the document below:
@@ -46,8 +50,6 @@#define DW_CFA_GNU_negative_offset_extended 0x2f#define DW_CFA_hi_user 0x3f -extern const u8 __eh_frame_start[], __eh_frame_end[]; -enum {PACIASP =0xd503233f ,AUTIASP =0xd50323bf ,
@@ -250,13 +252,3 @@ int scs_patch(const u8 eh_frame[], int size) }return 0 ; } - -asmlinkage void __init scs_patch_vmlinux(const u8 start[], const u8 end[]) -{ -if (!should_patch_pac_into_scs ()) -return ; - -scs_patch (start, end - start); - asm("ic ialluis" ); -isb (); -}
arch/arm64/kernel/pi/pi.h +14 -0
@@ -2,6 +2,8 @@// Copyright 2023 Google LLC // Author: Ard Biesheuvel <[email protected]> +#include <linux/types.h> +#define __prel64_initconst __section(".init.rodata.prel64")#define PREL64 (type, name) union { type *name; prel64_t name ## _prel; }
@@ -16,3 +18,15 @@ static inline void *prel64_to_pointer(const prel64_t *offset)return NULL ;return (void *)offset + *offset; } + +extern bool dynamic_scs_is_enabled; + +void init_feature_override (u64 boot_status,const void *fdt,int chosen); +u64 kaslr_early_init (void *fdt,int chosen); +void relocate_kernel (u64 offset); +int scs_patch (const u8 eh_frame[],int size); + +void map_range (u64 *pgd,u64 start,u64 end,u64 pa,pgprot_t prot, +int level,pte_t *tbl,bool may_use_cont,u64 va_offset); + +asmlinkage void early_map_kernel(u64 boot_status, void *fdt);
arch/arm64/kernel/pi/relocate.c +2 -0
@@ -7,6 +7,8 @@#include <linux/init.h> #include <linux/types.h> +#include "pi.h" +extern const Elf64_Rela rela_start[], rela_end[];extern const u64 relr_start[], relr_end[];
arch/arm64/kernel/setup.c +0 -7
@@ -283,13 +283,6 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)kaslr_init (); -/* -* If know now we are going to need KPTI then use non-global -* mappings from the start, avoiding the cost of rewriting -* everything later. -*/ - arm64_use_ng_mappings =kaslr_enabled () &&kaslr_requires_kpti (); -early_fixmap_init ();early_ioremap_init ();
arch/arm64/kernel/vmlinux.lds.S +2 -2
@@ -126,9 +126,9 @@ jiffies = jiffies_64; #ifdef CONFIG_UNWIND_TABLES #define UNWIND_DATA_SECTIONS \ .eh_frame : { \ - __eh_frame_start = .; \ + __pi___eh_frame_start = .; \ *(.eh_frame) \ - __eh_frame_end = .; \ + __pi___eh_frame_end = .; \ } #else #define UNWIND_DATA_SECTIONS
arch/arm64/mm/proc.S +1 -0
@@ -195,6 +195,7 @@ SYM_TYPED_FUNC_START(idmap_cpu_replace_ttbr1) ret SYM_FUNC_END(idmap_cpu_replace_ttbr1) +SYM_FUNC_ALIAS(__pi_idmap_cpu_replace_ttbr1, idmap_cpu_replace_ttbr1) .popsection #ifdef CONFIG_UNMAP_KERNEL_AT_EL0
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 26/50 arm64: mm: Use 48-bit virtual addressing for the permanent ID map
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Even though we support loading kernels anywhere in 48-bit addressable physical memory, we create the ID maps based on the number of levels that we happened to configure for the kernel VA and user VA spaces.
The reason for this is that the PGD/PUD/PMD based classification of translation levels, along with the associated folding when the number of levels is less than 5, does not permit creating a page table hierarchy of a set number of levels. This means that, for instance, on 39-bit VA kernels we need to configure an additional level above PGD level on the fly, and 36-bit VA kernels still only support 47-bit virtual addressing with this trick applied.
Now that we have a separate helper to populate page table hierarchies that does not define the levels in terms of PUDS/PMDS/etc at all, let's reuse it to create the permanent ID map with a fixed VA size of 48 bits.
--- arch/arm64/include/asm/kernel-pgtable.h | 3 ++ arch/arm64/kernel/head.S | 5 +++ arch/arm64/kvm/mmu.c | 15 +++------ arch/arm64/mm/mmu.c | 32 +++++++++++--------- arch/arm64/mm/proc.S | 9 ++---- 5 files changed, 32 insertions(+), 32 deletions(-)
arch/arm64/include/asm/kernel-pgtable.h +3 -0
@@ -35,6 +35,9 @@#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS)#endif +#define IDMAP_VA_BITS 48 +#define IDMAP_LEVELS ARM64_HW_PGTABLE_LEVELS(IDMAP_VA_BITS) +#define IDMAP_ROOT_LEVEL (4 - IDMAP_LEVELS)/* * A relocatable kernel may execute from an address that differs from the one at
arch/arm64/kernel/head.S +5 -0
@@ -729,6 +729,11 @@ SYM_FUNC_START_LOCAL(__no_granule_support) SYM_FUNC_END(__no_granule_support) SYM_FUNC_START_LOCAL(__primary_switch) + mrs x1, tcr_el1 + mov x2, #64 - VA_BITS + tcr_set_t0sz x1, x2 + msr tcr_el1, x1 + adrp x1, reserved_pg_dir adrp x2, init_idmap_pg_dir bl __enable_mmu
arch/arm64/kvm/mmu.c +4 -11
@@ -1874,16 +1874,9 @@ int __init kvm_mmu_init(u32 *hyp_va_bits)BUG_ON ((hyp_idmap_start ^ (hyp_idmap_end -1 )) &PAGE_MASK );/* -* The ID map may be configured to use an extended virtual address -* range. This is only the case if system RAM is out of range for the -* currently configured page size and VA_BITS_MIN, in which case we will -* also need the extended virtual range for the HYP ID map, or we won't -* be able to enable the EL2 MMU. -* -* However, in some cases the ID map may be configured for fewer than -* the number of VA bits used by the regular kernel stage 1. This -* happens when VA_BITS=52 and the kernel image is placed in PA space -* below 48 bits. +* The ID map is always configured for 48 bits of translation, which +* may be fewer than the number of VA bits used by the regular kernel +* stage 1, when VA_BITS=52. * * At EL2, there is only one TTBR register, and we can't switch between * translation tables *and* update TCR_EL2.T0SZ at the same time. Bottom
@@ -1894,7 +1887,7 @@ int __init kvm_mmu_init(u32 *hyp_va_bits)* 1 VA bits to assure that the hypervisor can both ID map its code page * and map any kernel memory. */ - idmap_bits =64 - ((idmap_t0sz &TCR_T0SZ_MASK ) >>TCR_T0SZ_OFFSET ); + idmap_bits =IDMAP_VA_BITS ; kernel_bits = vabits_actual; *hyp_va_bits =max (idmap_bits, kernel_bits);
arch/arm64/mm/mmu.c +17 -15
@@ -757,22 +757,21 @@ static void __init map_kernel(pgd_t *pgdp)kasan_copy_shadow (pgdp); } +void __pi_map_range (u64 *pgd,u64 start,u64 end,u64 pa,pgprot_t prot, +int level,pte_t *tbl,bool may_use_cont,u64 va_offset); + +static u8 idmap_ptes[IDMAP_LEVELS -1 ][PAGE_SIZE ]__aligned(PAGE_SIZE) __ro_after_init , + kpti_ptes[IDMAP_LEVELS -1 ][PAGE_SIZE ]__aligned(PAGE_SIZE) __ro_after_init ; +static void __init create_idmap (void ) {u64 start =__pa_symbol (__idmap_text_start); -u64 size =__pa_symbol (__idmap_text_end) - start; -pgd_t *pgd = idmap_pg_dir; -u64 pgd_phys; - -/* check if we need an additional level of translation */ -if (VA_BITS <48 && idmap_t0sz < (64 -VA_BITS_MIN )) { - pgd_phys =early_pgtable_alloc (PAGE_SHIFT ); -set_pgd (&idmap_pg_dir[start >>VA_BITS ], -__pgd (pgd_phys |P4D_TYPE_TABLE )); - pgd =__va (pgd_phys); - } -__create_pgd_mapping (pgd, start, start, size,PAGE_KERNEL_ROX , - early_pgtable_alloc,0 ); +u64 end =__pa_symbol (__idmap_text_end); +u64 ptep =__pa_symbol (idmap_ptes); + +__pi_map_range (&ptep, start, end, start,PAGE_KERNEL_ROX , +IDMAP_ROOT_LEVEL , (pte_t *)idmap_pg_dir, false, +__phys_to_virt (ptep) - ptep);if (IS_ENABLED (CONFIG_UNMAP_KERNEL_AT_EL0 )) {extern u32 __idmap_kpti_flag;
@@ -782,8 +781,10 @@ static void __init create_idmap(void)* The KPTI G-to-nG conversion code needs a read-write mapping * of its synchronization flag in the ID map. */ -__create_pgd_mapping (pgd, pa, pa,sizeof (u32),PAGE_KERNEL , - early_pgtable_alloc,0 ); + ptep =__pa_symbol (kpti_ptes); +__pi_map_range (&ptep, pa, pa +sizeof (u32), pa,PAGE_KERNEL , +IDMAP_ROOT_LEVEL , (pte_t *)idmap_pg_dir, false, +__phys_to_virt (ptep) - ptep); } }
@@ -808,6 +809,7 @@ void __init paging_init(void)memblock_allow_resize ();create_idmap (); + idmap_t0sz =TCR_T0SZ (IDMAP_VA_BITS ); }#ifdef CONFIG_MEMORY_HOTPLUG
arch/arm64/mm/proc.S +3 -6
@@ -421,9 +421,9 @@ SYM_FUNC_START(__cpu_setup) mair .req x17 tcr .req x16 mov_q mair, MAIR_EL1_SET - mov_q tcr, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \ - TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \ - TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS + mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS) | TCR_CACHE_FLAGS | \ + TCR_SMP_FLAGS | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \ + TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS tcr_clear_errata_bits tcr, x9, x5
@@ -431,10 +431,7 @@ SYM_FUNC_START(__cpu_setup) sub x9, xzr, x0 add x9, x9, #64 tcr_set_t1sz tcr, x9 -#else - idmap_get_t0sz x9 #endif - tcr_set_t0sz tcr, x9 /* * Set the IPS bits in TCR_EL1.
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 27/50 arm64: pgtable: Decouple PGDIR size macros from PGD/PUD/PMD levels
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The mapping from PGD/PUD/PMD to levels and shifts is very confusing, given that, due to folding, the shifts may be equal for different levels, if the macros are even #define'd to begin with.
In a subsequent patch, we will modify the ID mapping code to decouple the number of levels from the kernel's view of how these types are folded, so prepare for this by reformulating the macros without the use of these types.
Instead, use SWAPPER_BLOCK_SHIFT as the base quantity, and derive it from either PAGE_SHIFT or PMD_SHIFT, which -if defined at all- are defined unambiguously for a given page size, regardless of the number of configured levels.
--- arch/arm64/include/asm/kernel-pgtable.h | 65 ++++++-------------- 1 file changed, 19 insertions(+), 46 deletions(-)
arch/arm64/include/asm/kernel-pgtable.h +19 -46
@@ -13,27 +13,22 @@#include <asm/sparsemem.h> /* -* The linear mapping and the start of memory are both 2M aligned (per -* the arm64 booting.txt requirements). Hence we can use section mapping -* with 4K (section size = 2M) but not with 16K (section size = 32M) or -* 64K (section size = 512M). +* The physical and virtual addresses of the start of the kernel image are +* equal modulo 2 MiB (per the arm64 booting.txt requirements). Hence we can +* use section mapping with 4K (section size = 2M) but not with 16K (section +* size = 32M) or 64K (section size = 512M). */ - -/* -* The idmap and swapper page tables need some space reserved in the kernel -* image. Both require pgd, pud (4 levels only) and pmd tables to (section) -* map the kernel. With the 64K page configuration, swapper and idmap need to -* map to pte level. The swapper also maps the FDT (see __create_page_tables -* for more information). Note that the number of ID map translation levels -* could be increased on the fly if system RAM is out of reach for the default -* VA range, so pages required to map highest possible PA are reserved in all -* cases. -*/ -#ifdef CONFIG_ARM64_4K_PAGES -#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS - 1) +#if defined(PMD_SIZE ) &&PMD_SIZE <=MIN_KIMG_ALIGN +#define SWAPPER_BLOCK_SHIFT PMD_SHIFT +#define SWAPPER_SKIP_LEVEL 1#else -#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS) +#define SWAPPER_BLOCK_SHIFT PAGE_SHIFT +#define SWAPPER_SKIP_LEVEL 0#endif +#define SWAPPER_BLOCK_SIZE (UL(1) << SWAPPER_BLOCK_SHIFT) +#define SWAPPER_TABLE_SHIFT (SWAPPER_BLOCK_SHIFT + PAGE_SHIFT - 3) + +#define SWAPPER_PGTABLE_LEVELS (CONFIG_PGTABLE_LEVELS - SWAPPER_SKIP_LEVEL)#define IDMAP_VA_BITS 48#define IDMAP_LEVELS ARM64_HW_PGTABLE_LEVELS(IDMAP_VA_BITS)
@@ -53,24 +48,13 @@#define EARLY_ENTRIES (vstart, vend, shift, add) \ (SPAN_NR_ENTRIES(vstart, vend, shift) + (add)) -#define EARLY_PGDS (vstart, vend, add) (EARLY_ENTRIES(vstart, vend, PGDIR_SHIFT, add)) - -#if SWAPPER_PGTABLE_LEVELS >3 -#define EARLY_PUDS (vstart, vend, add) (EARLY_ENTRIES(vstart, vend, PUD_SHIFT, add)) -#else -#define EARLY_PUDS (vstart, vend, add) (0) -#endif +#define EARLY_LEVEL (lvl, vstart, vend, add) \ + (SWAPPER_PGTABLE_LEVELS > lvl ? EARLY_ENTRIES(vstart, vend, SWAPPER_BLOCK_SHIFT + lvl * (PAGE_SHIFT - 3), add) : 0) -#if SWAPPER_PGTABLE_LEVELS >2 -#define EARLY_PMDS (vstart, vend, add) (EARLY_ENTRIES(vstart, vend, SWAPPER_TABLE_SHIFT, add)) -#else -#define EARLY_PMDS (vstart, vend, add) (0) -#endif - -#define EARLY_PAGES (vstart, vend, add) ( 1/* PGDIR page */ \ - +EARLY_PGDS ((vstart), (vend), add)/* each PGDIR needs a next level page table */ \ - +EARLY_PUDS ((vstart), (vend), add)/* each PUD needs a next level page table */ \ - + EARLY_PMDS((vstart), (vend), add)) /* each PMD needs a next level page table */ +#define EARLY_PAGES (vstart, vend, add) (1/* PGDIR page */ \ + +EARLY_LEVEL (3 , (vstart), (vend), add)/* each entry needs a next level page table */ \ + +EARLY_LEVEL (2 , (vstart), (vend), add)/* each entry needs a next level page table */ \ + + EARLY_LEVEL(1, (vstart), (vend), add))/* each entry needs a next level page table */ #define INIT_DIR_SIZE (PAGE_SIZE * (EARLY_PAGES(KIMAGE_VADDR, _end, EXTRA_PAGE) + EARLY_SEGMENT_EXTRA_PAGES))/* the initial ID map may need two extra pages if it needs to be extended */
@@ -81,17 +65,6 @@#endif #define INIT_IDMAP_DIR_PAGES EARLY_PAGES(KIMAGE_VADDR, _end + MAX_FDT_SIZE + SWAPPER_BLOCK_SIZE, 1) -/* Initial memory map size */ -#ifdef CONFIG_ARM64_4K_PAGES -#define SWAPPER_BLOCK_SHIFT PMD_SHIFT -#define SWAPPER_BLOCK_SIZE PMD_SIZE -#define SWAPPER_TABLE_SHIFT PUD_SHIFT -#else -#define SWAPPER_BLOCK_SHIFT PAGE_SHIFT -#define SWAPPER_BLOCK_SIZE PAGE_SIZE -#define SWAPPER_TABLE_SHIFT PMD_SHIFT -#endif -/* The number of segments in the kernel image (text, rodata, inittext, initdata, data+bss) */ #define KERNEL_SEGMENT_COUNT 5
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 28/50 arm64: kernel: Create initial ID map from C code
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The asm code that creates the initial ID map is rather intricate and hard to follow. This is problematic because it makes adding support for things like LPA2 or WXN more difficult than necessary. Also, it is parameterized like the rest of the MM code to run with a configurable number of levels, which is rather pointless, given that all AArch64 CPUs implement support for 48-bit virtual addressing, and that many systems exist with DRAM located outside of the 39-bit addressable range, which is the only smaller VA size that is widely used, and we need additional tricks to make things work in that combination.
So let's bite the bullet, and rip out all the asm macros, and fiddly code, and replace it with a C implementation based on the newly added routines for creating the early kernel VA mappings. And while at it, create the initial ID map based on 48-bit virtual addressing as well, regardless of the number of configured levels for the kernel proper.
Note that this code may execute with the MMU and caches disabled, and is therefore not permitted to make unaligned accesses. This shouldn't generally happen in any case for the algorithm as implemented, but to be sure, let's pass -mstrict-align to the compiler just in case.
--- arch/arm64/include/asm/assembler.h | 14 - arch/arm64/include/asm/kernel-pgtable.h | 50 ++-- arch/arm64/include/asm/mmu_context.h | 6 +- arch/arm64/kernel/head.S | 267 ++------------------ arch/arm64/kernel/image-vars.h | 1 + arch/arm64/kernel/pi/Makefile | 3 + arch/arm64/kernel/pi/map_kernel.c | 18 ++ arch/arm64/kernel/pi/map_range.c | 12 + arch/arm64/kernel/pi/pi.h | 4 + arch/arm64/mm/mmu.c | 5 - arch/arm64/mm/proc.S | 3 +- 11 files changed, 88 insertions(+), 295 deletions(-)
arch/arm64/include/asm/assembler.h +0 -14
@@ -345,20 +345,6 @@ alternative_cb_end bfi \valreg, \t1sz, #TCR_T1SZ_OFFSET, #TCR_TxSZ_WIDTH .endm -/* -* idmap_get_t0sz - get the T0SZ value needed to cover the ID map -* -* Calculate the maximum allowed value for TCR_EL1.T0SZ so that the -* entire ID map region can be mapped. As T0SZ == (64 - #bits used), -* this number conveniently equals the number of leading zeroes in -* the physical address of _end. -*/ - .macro idmap_get_t0sz, reg - adrp \reg, _end - orr \reg, \reg, #(1 << VA_BITS_MIN) - 1 - clz \reg, \reg - .endm -/* * tcr_compute_pa_size - set TCR.(I)PS to the highest supported * ID_AA64MMFR0_EL1.PARange value
arch/arm64/include/asm/kernel-pgtable.h +23 -27
73 lines not on this page · raw message
arch/arm64/include/asm/mmu_context.h +2 -4
@@ -61,11 +61,9 @@ static inline void cpu_switch_mm(pgd_t *pgd, struct mm_struct *mm) }/* -* TCR.T0SZ value to use when the ID map is active. Usually equals -* TCR_T0SZ(VA_BITS), unless system RAM is positioned very high in -* physical memory, in which case it will be smaller. +* TCR.T0SZ value to use when the ID map is active. */ -extern int idmap_t0sz; +#define idmap_t0sz TCR_T0SZ(IDMAP_VA_BITS)/* * Ensure TCR.T0SZ is set to the provided value.
arch/arm64/kernel/head.S +23 -244
305 lines not on this page · raw message
arch/arm64/kernel/image-vars.h +1 -0
@@ -52,6 +52,7 @@ PROVIDE(__pi_cavium_erratum_27456_cpus = cavium_erratum_27456_cpus);PROVIDE (__pi__ctype = _ctype);PROVIDE (__pi_memstart_offset_seed = memstart_offset_seed); +PROVIDE (__pi_init_idmap_pg_dir = init_idmap_pg_dir);PROVIDE (__pi_init_pg_dir = init_pg_dir);PROVIDE (__pi_init_pg_end = init_pg_end);
arch/arm64/kernel/pi/Makefile +3 -0
@@ -11,6 +11,9 @@ KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \ -fno-asynchronous-unwind-tables -fno-unwind-tables \ $(call cc-option,-fno-addrsig) +# this code may run with the MMU off so disable unaligned accesses +CFLAGS_map_range.o += -mstrict-align + # remove SCS flags from all objects in this directory KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_SCS), $(KBUILD_CFLAGS)) # disable LTO
arch/arm64/kernel/pi/map_kernel.c +18 -0
@@ -128,6 +128,22 @@ static void __init map_kernel(u64 kaslr_offset, u64 va_offset, int root_level) } } +static void __init map_fdt (u64 fdt) +{ +static u8 ptes[INIT_IDMAP_FDT_SIZE ]__initdata __aligned(PAGE_SIZE) ; +u64 efdt = fdt +MAX_FDT_SIZE ; +u64 ptep = (u64 )ptes; + +/* +* Map up to MAX_FDT_SIZE bytes, but avoid overlap with +* the kernel image. +*/ +map_range (&ptep, fdt, (u64 )_text > fdt ?min ((u64 )_text, efdt) : efdt, + fdt,PAGE_KERNEL ,IDMAP_ROOT_LEVEL , + (pte_t *)init_idmap_pg_dir, false,0 ); +dsb (ishst); +} + asmlinkage void __init early_map_kernel(u64 boot_status, void *fdt) {static char const chosen_str[]__initconst ="/chosen" ;
@@ -136,6 +152,8 @@ asmlinkage void __init early_map_kernel(u64 boot_status, void *fdt)int root_level =4 -CONFIG_PGTABLE_LEVELS ;int chosen; +map_fdt ((u64 )fdt); +/* Clear BSS and the initial page tables */ memset (__bss_start,0 , (u64 )init_pg_end - (u64 )__bss_start);
arch/arm64/kernel/pi/map_range.c +12 -0
@@ -86,3 +86,15 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, tbl++; } } + +asmlinkage u64 __init create_init_idmap(pgd_t *pg_dir) +{ +u64 ptep = (u64 )pg_dir +PAGE_SIZE ; + +map_range (&ptep, (u64 )_stext, (u64 )__initdata_begin, (u64 )_stext, +PAGE_KERNEL_ROX ,IDMAP_ROOT_LEVEL , (pte_t *)pg_dir, false,0 ); +map_range (&ptep, (u64 )__initdata_begin, (u64 )_end, (u64 )__initdata_begin, +PAGE_KERNEL ,IDMAP_ROOT_LEVEL , (pte_t *)pg_dir, false,0 ); + +return ptep; +}
arch/arm64/kernel/pi/pi.h +4 -0
@@ -21,6 +21,8 @@ static inline void *prel64_to_pointer(const prel64_t *offset)extern bool dynamic_scs_is_enabled; +extern pgd_t init_idmap_pg_dir[]; +void init_feature_override (u64 boot_status,const void *fdt,int chosen);u64 kaslr_early_init (void *fdt,int chosen);void relocate_kernel (u64 offset);
@@ -30,3 +32,5 @@ void map_range(u64 *pgd, u64 start, u64 end, u64 pa, pgprot_t prot, int level, pte_t *tbl, bool may_use_cont, u64 va_offset); asmlinkage void early_map_kernel(u64 boot_status, void *fdt); + +asmlinkage u64 create_init_idmap(pgd_t *pgd);
arch/arm64/mm/mmu.c +0 -5
@@ -45,8 +45,6 @@#define NO_CONT_MAPPINGS BIT(1)#define NO_EXEC_MAPPINGS BIT(2)/* assumes FEAT_HPDS is not used */ -int idmap_t0sz__ro_after_init ; -#if VA_BITS >48 u64 vabits_actual__ro_after_init =VA_BITS_MIN ;EXPORT_SYMBOL (vabits_actual);
@@ -793,8 +791,6 @@ void __init paging_init(void)pgd_t *pgdp =pgd_set_fixmap (__pa_symbol (swapper_pg_dir));extern pgd_t init_idmap_pg_dir[]; - idmap_t0sz =63UL -__fls (__pa_symbol (_end) |GENMASK (VA_BITS_MIN -1 ,0 )); -map_kernel (pgdp);map_mem (pgdp);
@@ -809,7 +805,6 @@ void __init paging_init(void)memblock_allow_resize ();create_idmap (); - idmap_t0sz =TCR_T0SZ (IDMAP_VA_BITS ); }#ifdef CONFIG_MEMORY_HOTPLUG
arch/arm64/mm/proc.S +2 -1
@@ -200,7 +200,8 @@ SYM_FUNC_ALIAS(__pi_idmap_cpu_replace_ttbr1, idmap_cpu_replace_ttbr1) #ifdef CONFIG_UNMAP_KERNEL_AT_EL0 -#define KPTI_NG_PTE_FLAGS (PTE_ATTRINDX(MT_NORMAL) | SWAPPER_PTE_FLAGS | PTE_WRITE) +#define KPTI_NG_PTE_FLAGS (PTE_ATTRINDX(MT_NORMAL) | PTE_TYPE_PAGE | \ + PTE_AF | PTE_SHARED | PTE_UXN | PTE_WRITE) .pushsection ".idmap.text", "a"
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 29/50 arm64: mm: avoid fixmap for early swapper_pg_dir updates
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Early in the boot, when .rodata is still writable, we can poke swapper_pg_dir entries directly, and there is no need to go through the fixmap. After a future patch, we will enter the kernel with swapper_pg_dir already active, and early swapper_pg_dir updates for creating the fixmap page table hierarchy itself cannot go through the fixmap for obvious reaons. So let's keep track of whether rodata is writable, and update the descriptor directly in that case.
As the same reasoning applies to early KASAN init, make the function noinstr as well.
--- arch/arm64/mm/mmu.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
arch/arm64/mm/mmu.c +15 -1
@@ -55,6 +55,8 @@ EXPORT_SYMBOL(kimage_voffset);u32 __boot_cpu_mode[] = {BOOT_CPU_MODE_EL2 ,BOOT_CPU_MODE_EL1 }; +static bool rodata_is_rw__ro_after_init = true; +/* * The booting CPU updates the failed status @__early_cpu_boot_status, * with MMU turned off.
@@ -71,10 +73,21 @@ EXPORT_SYMBOL(empty_zero_page);static DEFINE_SPINLOCK (swapper_pgdir_lock);static DEFINE_MUTEX (fixmap_lock); -void set_swapper_pgd (pgd_t *pgdp,pgd_t pgd) +void noinstr set_swapper_pgd(pgd_t *pgdp, pgd_t pgd) {pgd_t *fixmap_pgdp; +/* +* Don't bother with the fixmap if swapper_pg_dir is still mapped +* writable in the kernel mapping. +*/ +if (rodata_is_rw) { +WRITE_ONCE (*pgdp, pgd); +dsb (ishst); +isb (); +return ; + } +spin_lock (&swapper_pgdir_lock); fixmap_pgdp =pgd_set_fixmap (__pa_symbol (pgdp));WRITE_ONCE (*fixmap_pgdp, pgd);
@@ -628,6 +641,7 @@ void mark_rodata_ro(void)* to cover NOTES and EXCEPTION_TABLE. */ section_size = (unsigned long )__init_begin - (unsigned long )__start_rodata; +WRITE_ONCE (rodata_is_rw, false);update_mapping_prot (__pa_symbol (__start_rodata), (unsigned long )__start_rodata, section_size,PAGE_KERNEL_RO );
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 30/50 arm64: mm: omit redundant remap of kernel image
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Now that the early kernel mapping is created with all the right attributes and segment boundaries, there is no longer a need to recreate it and switch to it. This also means we no longer have to copy the kasan shadow or some parts of the fixmap from one set of page tables to the other.
--- arch/arm64/include/asm/fixmap.h | 1 - arch/arm64/include/asm/kasan.h | 2 - arch/arm64/include/asm/mmu.h | 2 +- arch/arm64/kernel/image-vars.h | 1 + arch/arm64/kernel/pi/map_kernel.c | 6 +- arch/arm64/mm/fixmap.c | 34 -------- arch/arm64/mm/kasan_init.c | 15 ---- arch/arm64/mm/mmu.c | 85 ++++---------------- 8 files changed, 21 insertions(+), 125 deletions(-)
arch/arm64/include/asm/fixmap.h +0 -1
@@ -100,7 +100,6 @@ enum fixed_addresses {#define FIXMAP_PAGE_IO __pgprot(PROT_DEVICE_nGnRE)void __init early_fixmap_init (void ); -void __init fixmap_copy (pgd_t *pgdir);#define __early_set_fixmap __set_fixmap
arch/arm64/include/asm/kasan.h +0 -2
@@ -17,11 +17,9 @@ asmlinkage void kasan_early_init(void);void kasan_init (void ); -void kasan_copy_shadow (pgd_t *pgdir);#else static inline void kasan_init (void ) { } -static inline void kasan_copy_shadow (pgd_t *pgdir) { }#endif #endif
arch/arm64/include/asm/mmu.h +1 -1
@@ -110,7 +110,7 @@ static inline bool kaslr_requires_kpti(void) }#define INIT_MM_CONTEXT (name) \ - .pgd = init_pg_dir, + .pgd = swapper_pg_dir,#endif /* !__ASSEMBLY__ */ #endif
arch/arm64/kernel/image-vars.h +1 -0
@@ -55,6 +55,7 @@ PROVIDE(__pi_memstart_offset_seed = memstart_offset_seed);PROVIDE (__pi_init_idmap_pg_dir = init_idmap_pg_dir);PROVIDE (__pi_init_pg_dir = init_pg_dir);PROVIDE (__pi_init_pg_end = init_pg_end); +PROVIDE (__pi_swapper_pg_dir = swapper_pg_dir);PROVIDE (__pi__text = _text);PROVIDE (__pi__stext = _stext);
arch/arm64/kernel/pi/map_kernel.c +5 -1
@@ -124,8 +124,12 @@ static void __init map_kernel(u64 kaslr_offset, u64 va_offset, int root_level) text_prot, true, root_level);map_segment (init_pg_dir,NULL , va_offset, __inittext_begin, __inittext_end, text_prot, false, root_level); -dsb (ishst); } + +/* Copy the root page table to its final location */ +memcpy ((void *)swapper_pg_dir + va_offset, init_pg_dir,PGD_SIZE ); +dsb (ishst); +idmap_cpu_replace_ttbr1 (swapper_pg_dir); }static void __init map_fdt (u64 fdt)
arch/arm64/mm/fixmap.c +0 -34
@@ -170,37 +170,3 @@ void *__init fixmap_remap_fdt(phys_addr_t dt_phys, int *size, pgprot_t prot)return dt_virt; } - -/* -* Copy the fixmap region into a new pgdir. -*/ -void __init fixmap_copy (pgd_t *pgdir) -{ -if (!READ_ONCE (pgd_val (*pgd_offset_pgd (pgdir,FIXADDR_TOT_START )))) { -/* -* The fixmap falls in a separate pgd to the kernel, and doesn't -* live in the carveout for the swapper_pg_dir. We can simply -* re-use the existing dir for the fixmap. -*/ -set_pgd (pgd_offset_pgd (pgdir,FIXADDR_TOT_START ), -READ_ONCE (*pgd_offset_k (FIXADDR_TOT_START ))); - }else if (CONFIG_PGTABLE_LEVELS >3 ) { -pgd_t *bm_pgdp; -p4d_t *bm_p4dp; -pud_t *bm_pudp; -/* -* The fixmap shares its top level pgd entry with the kernel -* mapping. This can really only occur when we are running -* with 16k/4 levels, so we can simply reuse the pud level -* entry instead. -*/ -BUG_ON (!IS_ENABLED (CONFIG_ARM64_16K_PAGES )); - bm_pgdp =pgd_offset_pgd (pgdir,FIXADDR_TOT_START ); - bm_p4dp =p4d_offset (bm_pgdp,FIXADDR_TOT_START ); - bm_pudp =pud_set_fixmap_offset (bm_p4dp,FIXADDR_TOT_START ); -pud_populate (&init_mm, bm_pudp,lm_alias (bm_pmd)); -pud_clear_fixmap (); - }else { -BUG (); - } -}
arch/arm64/mm/kasan_init.c +0 -15
@@ -189,21 +189,6 @@ static void __init kasan_map_populate(unsigned long start, unsigned long end,kasan_pgd_populate (start &PAGE_MASK ,PAGE_ALIGN (end), node, false); } -/* -* Copy the current shadow region into a new pgdir. -*/ -void __init kasan_copy_shadow (pgd_t *pgdir) -{ -pgd_t *pgdp, *pgdp_new, *pgdp_end; - - pgdp =pgd_offset_k (KASAN_SHADOW_START ); - pgdp_end =pgd_offset_k (KASAN_SHADOW_END ); - pgdp_new =pgd_offset_pgd (pgdir,KASAN_SHADOW_START ); -do { -set_pgd (pgdp_new,READ_ONCE (*pgdp)); - }while (pgdp++, pgdp_new++, pgdp != pgdp_end); -} -static void __init clear_pgds (unsigned long start,unsigned long end) {
arch/arm64/mm/mmu.c +14 -71
127 lines not on this page · raw message
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 31/50 arm64: Revert "mm: provide idmap pointer to cpu_replace_ttbr1()"
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
This reverts commit 1682c45b920643c, which is no longer needed now that we create the permanent kernel mapping directly during early boot.
This is a RINO (revert in name only) given that some of the code has moved around, but the changes are straight-forward.
--- arch/arm64/include/asm/mmu_context.h | 17 ++++++----------- arch/arm64/mm/kasan_init.c | 4 ++-- arch/arm64/mm/mmu.c | 4 ++-- 3 files changed, 10 insertions(+), 15 deletions(-)
arch/arm64/include/asm/mmu_context.h +6 -11
@@ -108,18 +108,13 @@ static inline void cpu_uninstall_idmap(void)cpu_switch_mm (mm->pgd, mm); } -static inline void __cpu_install_idmap (pgd_t *idmap) +static inline void cpu_install_idmap (void ) {cpu_set_reserved_ttbr0 ();local_flush_tlb_all ();cpu_set_idmap_tcr_t0sz (); -cpu_switch_mm (lm_alias (idmap), &init_mm); -} - -static inline void cpu_install_idmap (void ) -{ -__cpu_install_idmap (idmap_pg_dir); +cpu_switch_mm (lm_alias (idmap_pg_dir), &init_mm); }/*
@@ -146,21 +141,21 @@ static inline void cpu_install_ttbr0(phys_addr_t ttbr0, unsigned long t0sz)isb (); } -void __cpu_replace_ttbr1 (pgd_t *pgdp,pgd_t *idmap,bool cnp); +void __cpu_replace_ttbr1 (pgd_t *pgdp,bool cnp);static inline void cpu_enable_swapper_cnp (void ) { -__cpu_replace_ttbr1 (lm_alias (swapper_pg_dir), idmap_pg_dir, true); +__cpu_replace_ttbr1 (lm_alias (swapper_pg_dir), true); } -static inline void cpu_replace_ttbr1 (pgd_t *pgdp,pgd_t *idmap) +static inline void cpu_replace_ttbr1 (pgd_t *pgdp) {/* * Only for early TTBR1 replacement before cpucaps are finalized and * before we've decided whether to use CNP. */ WARN_ON (system_capabilities_finalized ()); -__cpu_replace_ttbr1 (pgdp, idmap, false); +__cpu_replace_ttbr1 (pgdp, false); }/*
arch/arm64/mm/kasan_init.c +2 -2
@@ -225,7 +225,7 @@ static void __init kasan_init_shadow(void)*/ memcpy (tmp_pg_dir, swapper_pg_dir,sizeof (tmp_pg_dir));dsb (ishst); -cpu_replace_ttbr1 (lm_alias (tmp_pg_dir), idmap_pg_dir); +cpu_replace_ttbr1 (lm_alias (tmp_pg_dir));clear_pgds (KASAN_SHADOW_START ,KASAN_SHADOW_END );
@@ -261,7 +261,7 @@ static void __init kasan_init_shadow(void) PAGE_KERNEL_RO));memset (kasan_early_shadow_page,KASAN_SHADOW_INIT ,PAGE_SIZE ); -cpu_replace_ttbr1 (lm_alias (swapper_pg_dir), idmap_pg_dir); +cpu_replace_ttbr1 (lm_alias (swapper_pg_dir)); }static void __init kasan_init_depth (void )
arch/arm64/mm/mmu.c +2 -2
@@ -1445,7 +1445,7 @@ void ptep_modify_prot_commit(struct vm_area_struct *vma, unsigned long addr, pte* Atomically replaces the active TTBR1_EL1 PGD with a new VA-compatible PGD, * avoiding the possibility of conflicting TLB entries being allocated. */ -void __cpu_replace_ttbr1 (pgd_t *pgdp,pgd_t *idmap,bool cnp) +void __cpu_replace_ttbr1 (pgd_t *pgdp,bool cnp) {typedef void (ttbr_replace_func )(phys_addr_t );extern ttbr_replace_func idmap_cpu_replace_ttbr1;
@@ -1460,7 +1460,7 @@ void __cpu_replace_ttbr1(pgd_t *pgdp, pgd_t *idmap, bool cnp) replace_phys = (void *)__pa_symbol (idmap_cpu_replace_ttbr1); -__cpu_install_idmap (idmap); +cpu_install_idmap ();/* * We really don't want to take *any* exceptions while TTBR1 is
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 32/50 arm64: mm: Handle LVA support as a CPU feature
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Currently, we detect CPU support for 52-bit virtual addressing (LVA) extremely early, before creating the kernel page tables or enabling the MMU. We cannot override the feature this early, and so large virtual addressing is always enabled on CPUs that implement support for it if the software support for it was enabled at build time. It also means we rely on non-trivial code in asm to deal with this feature.
Given that both the ID map and the TTBR1 mapping of the kernel image are guaranteed to be 48-bit addressable, it is not actually necessary to enable support this early, and instead, we can model it as a CPU feature. That way, we can rely on code patching to get the correct TCR.T1SZ values programmed on secondary boot and resume from suspend.
On the primary boot path, we simply enable the MMU with 48-bit virtual addressing initially, and update TCR.T1SZ if LVA is supported from C code, right before creating the kernel mapping. Given that TTBR1 still points to reserved_pg_dir at this point, updating TCR.T1SZ should be safe without the need for explicit TLB maintenance.
Since this gets rid of all accesses to the vabits_actual variable from asm code that occurred before TCR.T1SZ had been programmed, we no longer have a need for this variable, and we can replace it with a C expression that produces the correct value directly, based on the value of TCR.T1SZ.
--- arch/arm64/include/asm/cpufeature.h | 9 ++++++ arch/arm64/include/asm/memory.h | 13 ++++++++- arch/arm64/kernel/cpufeature.c | 13 +++++++++ arch/arm64/kernel/head.S | 29 +++++--------------- arch/arm64/kernel/image-vars.h | 1 - arch/arm64/kernel/pi/map_kernel.c | 3 ++ arch/arm64/kernel/sleep.S | 3 -- arch/arm64/mm/mmu.c | 5 ---- arch/arm64/mm/proc.S | 9 +++--- arch/arm64/tools/cpucaps | 1 + 10 files changed, 49 insertions(+), 37 deletions(-)
arch/arm64/include/asm/cpufeature.h +9 -0
@@ -995,6 +995,15 @@ static inline bool cpu_has_pac(void) &id_aa64isar2_override); } +static inline bool cpu_has_lva (void ) +{ +u64 mmfr2; + + mmfr2 =read_sysreg_s (SYS_ID_AA64MMFR2_EL1 ); +return cpuid_feature_extract_unsigned_field (mmfr2, + ID_AA64MMFR2_EL1_VARange_SHIFT); +} +#endif /* __ASSEMBLY__ */ #endif
arch/arm64/include/asm/memory.h +12 -1
@@ -209,9 +209,20 @@#include <asm/boot.h> #include <asm/bug.h> #include <asm/sections.h> +#include <asm/sysreg.h> + +static inline u64 __pure read_tcr (void ) +{ +u64 tcr; + +// read_sysreg() uses asm volatile, so avoid it here + asm("mrs %0, tcr_el1" :"=r" (tcr)); +return tcr; +}#if VA_BITS >48 -extern u64 vabits_actual; +// For reasons of #include hell, we can't use TCR_T1SZ_OFFSET/TCR_T1SZ_MASK here +#define vabits_actual (64 - ((read_tcr() >> 16) & 63))#else #define vabits_actual ((u64)VA_BITS)#endif
arch/arm64/kernel/cpufeature.c +13 -0
@@ -2692,6 +2692,19 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .type = ARM64_CPUCAP_SYSTEM_FEATURE, .matches = has_lpa2, }, +#ifdef CONFIG_ARM64_VA_BITS_52 + { + .desc = "52-bit Virtual Addressing (LVA)", + .capability = ARM64_HAS_VA52, + .type = ARM64_CPUCAP_BOOT_CPU_FEATURE, + .sys_reg = SYS_ID_AA64MMFR2_EL1, + .sign = FTR_UNSIGNED, + .field_width = 4, + .field_pos = ID_AA64MMFR2_EL1_VARange_SHIFT, + .matches = has_cpuid_feature, + .min_field_value = ID_AA64MMFR2_EL1_VARange_52, + }, +#endif {}, };
arch/arm64/kernel/head.S +7 -22
@@ -80,7 +80,6 @@ * x19 primary_entry() .. start_kernel() whether we entered with the MMU on * x20 primary_entry() .. __primary_switch() CPU boot mode * x21 primary_entry() .. start_kernel() FDT pointer passed at boot in x0 - * x25 primary_entry() .. start_kernel() supported VA size */ SYM_CODE_START(primary_entry) bl record_mmu_state
@@ -125,14 +124,6 @@ SYM_CODE_START(primary_entry) * On return, the CPU will be ready for the MMU to be turned on and * the TCR will have been set. */ -#if VA_BITS > 48 - mrs_s x0, SYS_ID_AA64MMFR2_EL1 - tst x0, ID_AA64MMFR2_EL1_VARange_MASK - mov x0, #VA_BITS - mov x25, #VA_BITS_MIN - csel x25, x25, x0, eq - mov x0, x25 -#endif bl __cpu_setup // initialise processor b __primary_switch SYM_CODE_END(primary_entry)
@@ -242,11 +233,6 @@ SYM_FUNC_START_LOCAL(__primary_switched) mov x0, x20 bl set_cpu_boot_mode_flag -#if VA_BITS > 48 - adr_l x8, vabits_actual // Set this early so KASAN early init - str x25, [x8] // ... observes the correct value - dc civac, x8 // Make visible to booting secondaries -#endif #if defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KASAN_SW_TAGS) bl kasan_early_init #endif
@@ -376,10 +362,13 @@ SYM_FUNC_START_LOCAL(secondary_startup) * Common entry point for secondary CPUs. */ mov x20, x0 // preserve boot mode + +#ifdef CONFIG_ARM64_VA_BITS_52 +alternative_if ARM64_HAS_VA52 bl __cpu_secondary_check52bitva -#if VA_BITS > 48 - ldr_l x0, vabits_actual +alternative_else_nop_endif #endif + bl __cpu_setup // initialise processor adrp x1, swapper_pg_dir adrp x2, idmap_pg_dir
@@ -482,12 +471,8 @@ SYM_FUNC_START(__enable_mmu) ret SYM_FUNC_END(__enable_mmu) +#ifdef CONFIG_ARM64_VA_BITS_52 SYM_FUNC_START(__cpu_secondary_check52bitva) -#if VA_BITS > 48 - ldr_l x0, vabits_actual - cmp x0, #52 - b.ne 2f - mrs_s x0, SYS_ID_AA64MMFR2_EL1 and x0, x0, ID_AA64MMFR2_EL1_VARange_MASK cbnz x0, 2f
@@ -498,9 +483,9 @@ SYM_FUNC_START(__cpu_secondary_check52bitva) wfi b 1b -#endif 2: ret SYM_FUNC_END(__cpu_secondary_check52bitva) +#endif SYM_FUNC_START_LOCAL(__no_granule_support) /* Indicate that this CPU can't boot and is stuck in the kernel */
arch/arm64/kernel/image-vars.h +0 -1
@@ -36,7 +36,6 @@ PROVIDE(__pi___memcpy = __pi_memcpy);PROVIDE (__pi___memmove = __pi_memmove);PROVIDE (__pi___memset = __pi_memset); -PROVIDE (__pi_vabits_actual = vabits_actual);PROVIDE (__pi_id_aa64isar1_override = id_aa64isar1_override);PROVIDE (__pi_id_aa64isar2_override = id_aa64isar2_override);PROVIDE (__pi_id_aa64mmfr1_override = id_aa64mmfr1_override);
arch/arm64/kernel/pi/map_kernel.c +3 -0
@@ -165,6 +165,9 @@ asmlinkage void __init early_map_kernel(u64 boot_status, void *fdt) chosen =fdt_path_offset (fdt, chosen_str);init_feature_override (boot_status, fdt, chosen); +if (VA_BITS >VA_BITS_MIN &&cpu_has_lva ()) +sysreg_clear_set (tcr_el1,TCR_T1SZ_MASK ,TCR_T1SZ (VA_BITS )); +/* * The virtual KASLR displacement modulo 2MiB is decided by the * physical placement of the image, as otherwise, we might not be able
arch/arm64/kernel/sleep.S +0 -3
@@ -102,9 +102,6 @@ SYM_CODE_START(cpu_resume) mov x0, xzr bl init_kernel_el mov x19, x0 // preserve boot mode -#if VA_BITS > 48 - ldr_l x0, vabits_actual -#endif bl __cpu_setup /* enable the MMU early - so we can access sleep_save_stash by va */ adrp x1, swapper_pg_dir
arch/arm64/mm/mmu.c +0 -5
@@ -45,11 +45,6 @@#define NO_CONT_MAPPINGS BIT(1)#define NO_EXEC_MAPPINGS BIT(2)/* assumes FEAT_HPDS is not used */ -#if VA_BITS >48 -u64 vabits_actual__ro_after_init =VA_BITS_MIN ; -EXPORT_SYMBOL (vabits_actual); -#endif -u64 kimage_voffset__ro_after_init ;EXPORT_SYMBOL (kimage_voffset);
arch/arm64/mm/proc.S +4 -5
@@ -397,8 +397,6 @@ SYM_FUNC_END(idmap_kpti_install_ng_mappings) * * Initialise the processor for turning the MMU on. * - * Input: - * x0 - actual number of VA bits (ignored unless VA_BITS > 48) * Output: * Return in x0 the value of the SCTLR_EL1 register. */
@@ -422,16 +420,17 @@ SYM_FUNC_START(__cpu_setup) mair .req x17 tcr .req x16 mov_q mair, MAIR_EL1_SET - mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS) | TCR_CACHE_FLAGS | \ + mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS_MIN) | TCR_CACHE_FLAGS | \ TCR_SMP_FLAGS | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \ TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS tcr_clear_errata_bits tcr, x9, x5 #ifdef CONFIG_ARM64_VA_BITS_52 - sub x9, xzr, x0 - add x9, x9, #64 + mov x9, #64 - VA_BITS +alternative_if ARM64_HAS_VA52 tcr_set_t1sz tcr, x9 +alternative_else_nop_endif #endif /*
arch/arm64/tools/cpucaps +1 -0
@@ -50,6 +50,7 @@ HAS_STAGE2_FWB HAS_TCR2 HAS_TIDCP1 HAS_TLB_RANGE +HAS_VA52 HAS_VIRT_HOST_EXTN HAS_WFXT HW_DBM
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 33/50 arm64: mm: Add feature override support for LVA
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add support for overriding the VARange field of the MMFR2 CPU ID register. This permits the associated LVA feature to be overridden early enough for the boot code that creates the kernel mapping to take it into account.
Given that LPA2 implies LVA, disabling the latter should disable the former as well. So override the ID_AA64MMFR0.TGran field of the current page size as well if it advertises support for 52-bit addressing.
--- arch/arm64/include/asm/assembler.h | 17 ++++++----- arch/arm64/include/asm/cpufeature.h | 4 +++ arch/arm64/kernel/cpufeature.c | 8 +++-- arch/arm64/kernel/image-vars.h | 2 ++ arch/arm64/kernel/pi/idreg-override.c | 31 ++++++++++++++++++++ 5 files changed, 53 insertions(+), 9 deletions(-)
arch/arm64/include/asm/assembler.h +10 -7
@@ -576,18 +576,21 @@ alternative_endif .endm/* -* Offset ttbr1 to allow for 48-bit kernel VAs set with 52-bit PTRS_PER_PGD. +* If the kernel is built for 52-bit virtual addressing but the hardware only +* supports 48 bits, we cannot program the pgdir address into TTBR1 directly, +* but we have to add an offset so that the TTBR1 address corresponds with the +* pgdir entry that covers the lowest 48-bit addressable VA. +* * orr is used as it can cover the immediate value (and is idempotent). -* In future this may be nop'ed out when dealing with 52-bit kernel VAs. * ttbr: Value of ttbr to set, modified. */ .macro offset_ttbr1, ttbr, tmp#ifdef CONFIG_ARM64_VA_BITS_52 - mrs_s \tmp, SYS_ID_AA64MMFR2_EL1 - and \tmp, \tmp, #(0xf << ID_AA64MMFR2_EL1_VARange_SHIFT) - cbnz \tmp, .Lskipoffs_\@ - orr \ttbr, \ttbr, #TTBR1_BADDR_4852_OFFSET -.Lskipoffs_\@ : + mrs \tmp, tcr_el1 + and \tmp, \tmp, #TCR_T1SZ_MASK + cmp \tmp, #TCR_T1SZ(VA_BITS_MIN) + orr \tmp, \ttbr, #TTBR1_BADDR_4852_OFFSET + csel \ttbr, \tmp, \ttbr, eq #endif .endm
arch/arm64/include/asm/cpufeature.h +4 -0
@@ -906,7 +906,9 @@ static inline unsigned int get_vmid_bits(u64 mmfr1)s64 arm64_ftr_safe_value (const struct arm64_ftr_bits *ftrp,s64 new,s64 cur);struct arm64_ftr_reg *get_arm64_ftr_reg (u32 sys_id); +extern struct arm64_ftr_override id_aa64mmfr0_override;extern struct arm64_ftr_override id_aa64mmfr1_override; +extern struct arm64_ftr_override id_aa64mmfr2_override;extern struct arm64_ftr_override id_aa64pfr0_override;extern struct arm64_ftr_override id_aa64pfr1_override;extern struct arm64_ftr_override id_aa64zfr0_override;
@@ -1000,6 +1002,8 @@ static inline bool cpu_has_lva(void)u64 mmfr2; mmfr2 =read_sysreg_s (SYS_ID_AA64MMFR2_EL1 ); + mmfr2 &= ~id_aa64mmfr2_override.mask; + mmfr2 |= id_aa64mmfr2_override.val;return cpuid_feature_extract_unsigned_field (mmfr2, ID_AA64MMFR2_EL1_VARange_SHIFT); }
arch/arm64/kernel/cpufeature.c +6 -2
@@ -655,7 +655,9 @@ static const struct arm64_ftr_bits ftr_raz[] = {#define ARM64_FTR_REG (id, table) \ __ARM64_FTR_REG_OVERRIDE(#id, id, table, &no_override) +struct arm64_ftr_override id_aa64mmfr0_override;struct arm64_ftr_override id_aa64mmfr1_override; +struct arm64_ftr_override id_aa64mmfr2_override;struct arm64_ftr_override id_aa64pfr0_override;struct arm64_ftr_override id_aa64pfr1_override;struct arm64_ftr_override id_aa64zfr0_override;
@@ -719,10 +721,12 @@ static const struct __ftr_reg_entry { &id_aa64isar2_override),/* Op1 = 0, CRn = 0, CRm = 7 */ -ARM64_FTR_REG (SYS_ID_AA64MMFR0_EL1 , ftr_id_aa64mmfr0), +ARM64_FTR_REG_OVERRIDE (SYS_ID_AA64MMFR0_EL1 , ftr_id_aa64mmfr0, + &id_aa64mmfr0_override),ARM64_FTR_REG_OVERRIDE (SYS_ID_AA64MMFR1_EL1 , ftr_id_aa64mmfr1, &id_aa64mmfr1_override), -ARM64_FTR_REG (SYS_ID_AA64MMFR2_EL1 , ftr_id_aa64mmfr2), +ARM64_FTR_REG_OVERRIDE (SYS_ID_AA64MMFR2_EL1 , ftr_id_aa64mmfr2, + &id_aa64mmfr2_override), ARM64_FTR_REG(SYS_ID_AA64MMFR3_EL1, ftr_id_aa64mmfr3),/* Op1 = 1, CRn = 0, CRm = 0 */
arch/arm64/kernel/image-vars.h +2 -0
@@ -38,7 +38,9 @@ PROVIDE(__pi___memset = __pi_memset);PROVIDE (__pi_id_aa64isar1_override = id_aa64isar1_override);PROVIDE (__pi_id_aa64isar2_override = id_aa64isar2_override); +PROVIDE (__pi_id_aa64mmfr0_override = id_aa64mmfr0_override);PROVIDE (__pi_id_aa64mmfr1_override = id_aa64mmfr1_override); +PROVIDE (__pi_id_aa64mmfr2_override = id_aa64mmfr2_override);PROVIDE (__pi_id_aa64pfr0_override = id_aa64pfr0_override);PROVIDE (__pi_id_aa64pfr1_override = id_aa64pfr1_override);PROVIDE (__pi_id_aa64smfr0_override = id_aa64smfr0_override);
arch/arm64/kernel/pi/idreg-override.c +31 -0
@@ -59,6 +59,35 @@ static const struct ftr_set_desc mmfr1 __prel64_initconst = { }, }; + +static bool __init mmfr2_varange_filter (u64 val) +{ +int __maybe_unused feat; + +if (val) +return false; + +#ifdef CONFIG_ARM64_LPA2 + feat =cpuid_feature_extract_signed_field (read_sysreg (id_aa64mmfr0_el1), +ID_AA64MMFR0_EL1_TGRAN_SHIFT ); +if (feat >=ID_AA64MMFR0_EL1_TGRAN_LPA2 ) { + id_aa64mmfr0_override.val |= + (ID_AA64MMFR0_EL1_TGRAN_LPA2 -1 ) <<ID_AA64MMFR0_EL1_TGRAN_SHIFT ; + id_aa64mmfr0_override.mask |=0xfU <<ID_AA64MMFR0_EL1_TGRAN_SHIFT ; + } +#endif +return true; +} + +static const struct ftr_set_desc mmfr2 __prel64_initconst = { + .name ="id_aa64mmfr2" , + .override = &id_aa64mmfr2_override, + .fields = { +FIELD ("varange" , ID_AA64MMFR2_EL1_VARange_SHIFT, mmfr2_varange_filter), + {} + }, +}; +static bool __init pfr0_sve_filter (u64 val) {/*
@@ -167,6 +196,7 @@ static const struct ftr_set_desc sw_features __prel64_initconst = {static const PREL64(const struct ftr_set_desc, reg) regs[] __prel64_initconst = { { &mmfr1 }, + { &mmfr2 }, { &pfr0 }, { &pfr1 }, { &isar1 },
@@ -192,6 +222,7 @@ static const struct { { "arm64.nomte", "id_aa64pfr1.mte=0" }, { "nokaslr", "arm64_sw.nokaslr=1" }, { "rodata=off", "arm64_sw.rodataoff=1" }, + { "arm64.nolva", "id_aa64mmfr2.varange=0" }, };static int __init parse_hexdigit (const char *p,u64 *v)
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 34/50 arm64: Avoid #define'ing PTE_MAYBE_NG to 0x0 for asm use
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The PROT_* macros resolve to expressions that are only valid in C and not in assembler, and so they are only usable from C code. Currently, we make an exception for the permission indirection init code in proc.S, which doesn't care about the bits that are conditionally set, and so we just #define PTE_MAYBE_NG to 0x0 for any assembler file that includes these definitions.
This is dodgy because this means that PROT_NORMAL and friends is generally available in asm code, but defined in a way that deviates from the definition that C code will observe, which might lead to hard to diagnose issues down the road.
So instead, #define PTE_MAYBE_NG only in the place where the PIE constants are evaluated, and #undef it again right after. This allows us to drop the #define from pgtable-prot.h, and avoid the risk of deviating definitions between asm and C.
--- arch/arm64/include/asm/pgtable-prot.h | 4 ---- arch/arm64/mm/proc.S | 13 +++++++++++++ 2 files changed, 13 insertions(+), 4 deletions(-)
arch/arm64/include/asm/pgtable-prot.h +0 -4
@@ -57,10 +57,6 @@#define _PAGE_READONLY_EXEC (_PAGE_DEFAULT | PTE_USER | PTE_RDONLY | PTE_NG | PTE_PXN)#define _PAGE_EXECONLY (_PAGE_DEFAULT | PTE_RDONLY | PTE_NG | PTE_PXN) -#ifdef __ASSEMBLY__ -#define PTE_MAYBE_NG 0 -#endif -#ifndef __ASSEMBLY__#include <asm/cpufeature.h>
arch/arm64/mm/proc.S +13 -0
@@ -456,11 +456,24 @@ alternative_else_nop_endif ubfx x1, x1, #ID_AA64MMFR3_EL1_S1PIE_SHIFT, #4 cbz x1, .Lskip_indirection + /* + * The PROT_* macros describing the various memory types may resolve to + * C expressions if they include the PTE_MAYBE_* macros, and so they + * can only be used from C code. The PIE_E* constants below are also + * defined in terms of those macros, but will mask out those + * PTE_MAYBE_* constants, whether they are set or not. So #define them + * as 0x0 here so we can evaluate the PIE_E* constants in asm context. + */ + +#define PTE_MAYBE_NG 0 + mov_q x0, PIE_E0 msr REG_PIRE0_EL1, x0 mov_q x0, PIE_E1 msr REG_PIR_EL1, x0 +#undef PTE_MAYBE_NG + mov x0, TCR2_EL1x_PIE msr REG_TCR2_EL1, x0
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 35/50 arm64: Add ESR decoding for exceptions involving translation level -1
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The LPA2 feature introduces new FSC values to report abort exceptions related to translation level -1. Define these and wire them up.
Reuse the new ESR FSC classification helpers that arrived via the KVM arm64 tree, and update the one for translation faults to check specifically for a translation fault at level -1. (Access flag or permission faults cannot occur at level -1 because they alway involve a descriptor at the superior level so changing those helpers is not needed).
--- arch/arm64/include/asm/esr.h | 13 ++++----- arch/arm64/include/asm/kvm_emulate.h | 10 ++----- arch/arm64/mm/fault.c | 30 +++++++------------- 3 files changed, 18 insertions(+), 35 deletions(-)
arch/arm64/include/asm/esr.h +5 -8
@@ -117,15 +117,9 @@#define ESR_ELx_FSC_ACCESS (0x08)#define ESR_ELx_FSC_FAULT (0x04)#define ESR_ELx_FSC_PERM (0x0C) -#define ESR_ELx_FSC_SEA_TTW0 (0x14) -#define ESR_ELx_FSC_SEA_TTW1 (0x15) -#define ESR_ELx_FSC_SEA_TTW2 (0x16) -#define ESR_ELx_FSC_SEA_TTW3 (0x17) +#define ESR_ELx_FSC_SEA_TTW (n) (0x14 + (n))#define ESR_ELx_FSC_SECC (0x18) -#define ESR_ELx_FSC_SECC_TTW0 (0x1c) -#define ESR_ELx_FSC_SECC_TTW1 (0x1d) -#define ESR_ELx_FSC_SECC_TTW2 (0x1e) -#define ESR_ELx_FSC_SECC_TTW3 (0x1f) +#define ESR_ELx_FSC_SECC_TTW (n) (0x1c + (n))/* ISS field definitions for Data Aborts */ #define ESR_ELx_ISV_SHIFT (24)
@@ -394,6 +388,9 @@ static inline bool esr_is_data_abort(unsigned long esr)static inline bool esr_fsc_is_translation_fault (unsigned long esr) { +/* Translation fault, level -1 */ +if ((esr & ESR_ELx_FSC) ==0b101011 ) +return true;return (esr & ESR_ELx_FSC_TYPE) == ESR_ELx_FSC_FAULT; }
arch/arm64/include/asm/kvm_emulate.h +2 -8
@@ -425,15 +425,9 @@ static __always_inline bool kvm_vcpu_abt_issea(const struct kvm_vcpu *vcpu) {switch (kvm_vcpu_trap_get_fault (vcpu)) {case ESR_ELx_FSC_EXTABT: -case ESR_ELx_FSC_SEA_TTW0: -case ESR_ELx_FSC_SEA_TTW1: -case ESR_ELx_FSC_SEA_TTW2: -case ESR_ELx_FSC_SEA_TTW3: + case ESR_ELx_FSC_SEA_TTW(-1) ... ESR_ELx_FSC_SEA_TTW(3):case ESR_ELx_FSC_SECC: -case ESR_ELx_FSC_SECC_TTW0: -case ESR_ELx_FSC_SECC_TTW1: -case ESR_ELx_FSC_SECC_TTW2: -case ESR_ELx_FSC_SECC_TTW3: + case ESR_ELx_FSC_SECC_TTW(-1) ... ESR_ELx_FSC_SECC_TTW(3):return true;default :return false;
arch/arm64/mm/fault.c +11 -19
@@ -257,16 +257,14 @@ static bool is_el1_data_abort(unsigned long esr)static inline bool is_el1_permission_fault (unsigned long addr,unsigned long esr,struct pt_regs *regs) { -unsigned long fsc_type = esr & ESR_ELx_FSC_TYPE; -if (!is_el1_data_abort (esr) && !is_el1_instruction_abort (esr))return false; -if (fsc_type == ESR_ELx_FSC_PERM) +if (esr_fsc_is_permission_fault (esr))return true;if (is_ttbr0_addr (addr) &&system_uses_ttbr0_pan ()) -return fsc_type == ESR_ELx_FSC_FAULT && +return esr_fsc_is_translation_fault (esr) && (regs->pstate &PSR_PAN_BIT );return false;
@@ -279,8 +277,7 @@ static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr,unsigned long flags;u64 par, dfsc; -if (!is_el1_data_abort (esr) || - (esr & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT) +if (!is_el1_data_abort (esr) || !esr_fsc_is_translation_fault (esr))return false;local_irq_save (flags);
@@ -301,7 +298,7 @@ static bool __kprobes is_spurious_el1_translation_fault(unsigned long addr,* treat the translation fault as spurious. */ dfsc =FIELD_GET (SYS_PAR_EL1_FST , par); -return (dfsc & ESR_ELx_FSC_TYPE) != ESR_ELx_FSC_FAULT; +return !esr_fsc_is_translation_fault (dfsc); } static void die_kernel_fault(const char *msg, unsigned long addr,
@@ -368,11 +365,6 @@ static bool is_el1_mte_sync_tag_check_fault(unsigned long esr)return false; } -static bool is_translation_fault (unsigned long esr) -{ -return (esr & ESR_ELx_FSC_TYPE) == ESR_ELx_FSC_FAULT; -} -static void __do_kernel_fault (unsigned long addr,unsigned long esr,struct pt_regs *regs) {
@@ -405,7 +397,7 @@ static void __do_kernel_fault(unsigned long addr, unsigned long esr, }else if (addr <PAGE_SIZE ) { msg ="NULL pointer dereference" ; }else { -if (is_translation_fault (esr) && +if (esr_fsc_is_translation_fault (esr) &&kfence_handle_page_fault (addr, esr & ESR_ELx_WNR, regs))return ;
@@ -782,18 +774,18 @@ static const struct fault_info fault_info[] = { { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" }, { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 8" }, + { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 0 access flag fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 12" }, + { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 0 permission fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" }, { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" }, { do_sea, SIGBUS, BUS_OBJERR, "synchronous external abort" }, { do_tag_check_fault, SIGSEGV, SEGV_MTESERR, "synchronous tag check fault" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 18" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 19" }, + { do_sea, SIGKILL, SI_KERNEL, "level -1 (translation table walk)" }, { do_sea, SIGKILL, SI_KERNEL, "level 0 (translation table walk)" }, { do_sea, SIGKILL, SI_KERNEL, "level 1 (translation table walk)" }, { do_sea, SIGKILL, SI_KERNEL, "level 2 (translation table walk)" },
@@ -801,7 +793,7 @@ static const struct fault_info fault_info[] = { { do_sea, SIGBUS, BUS_OBJERR, "synchronous parity or ECC error" }, // Reserved when RAS is implemented { do_bad, SIGKILL, SI_KERNEL, "unknown 25" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 26" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 27" }, + { do_sea, SIGKILL, SI_KERNEL, "level -1 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented { do_sea, SIGKILL, SI_KERNEL, "level 0 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented { do_sea, SIGKILL, SI_KERNEL, "level 1 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented { do_sea, SIGKILL, SI_KERNEL, "level 2 synchronous parity error (translation table walk)" }, // Reserved when RAS is implemented
@@ -815,9 +807,9 @@ static const struct fault_info fault_info[] = { { do_bad, SIGKILL, SI_KERNEL, "unknown 38" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 39" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 40" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 41" }, + { do_bad, SIGKILL, SI_KERNEL, "level -1 address size fault" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 42" }, - { do_bad, SIGKILL, SI_KERNEL, "unknown 43" }, + { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level -1 translation fault" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 44" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 45" }, { do_bad, SIGKILL, SI_KERNEL, "unknown 46" },
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 36/50 arm64: mm: Wire up TCR.DS bit to PTE shareability fields
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
When LPA2 is enabled, bits 8 and 9 of page and block descriptors become part of the output address instead of carrying shareability attributes for the region in question.
So avoid setting these bits if TCR.DS == 1, which means LPA2 is enabled.
--- arch/arm64/Kconfig | 4 ++++ arch/arm64/include/asm/pgtable-hwdef.h | 1 + arch/arm64/include/asm/pgtable-prot.h | 16 ++++++++++++++-- arch/arm64/mm/mmap.c | 4 ++++ arch/arm64/mm/proc.S | 2 ++ 5 files changed, 25 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +4 -0
@@ -1377,6 +1377,10 @@ config ARM64_PA_BITS default 48 if ARM64_PA_BITS_48 default 52 if ARM64_PA_BITS_52 +config ARM64_LPA2 + def_bool y + depends on ARM64_PA_BITS_52 && !ARM64_64K_PAGES + choice prompt "Endianness" default CPU_LITTLE_ENDIAN
arch/arm64/include/asm/pgtable-hwdef.h +1 -0
@@ -284,6 +284,7 @@#define TCR_E0PD1 (UL(1) << 56)#define TCR_TCMA0 (UL(1) << 57)#define TCR_TCMA1 (UL(1) << 58) +#define TCR_DS (UL(1) << 59)/* * TTBR.
arch/arm64/include/asm/pgtable-prot.h +14 -2
@@ -30,8 +30,8 @@#define _PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_SHARED)#define _PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S) -#define PROT_DEFAULT (_PROT_DEFAULT | PTE_MAYBE_NG) -#define PROT_SECT_DEFAULT (_PROT_SECT_DEFAULT | PMD_MAYBE_NG) +#define PROT_DEFAULT (PTE_TYPE_PAGE | PTE_MAYBE_NG | PTE_MAYBE_SHARED | PTE_AF) +#define PROT_SECT_DEFAULT (PMD_TYPE_SECT | PMD_MAYBE_NG | PMD_MAYBE_SHARED | PMD_SECT_AF)#define PROT_DEVICE_nGnRnE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRnE))#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_PXN | PTE_UXN | PTE_WRITE | PTE_ATTRINDX(MT_DEVICE_nGnRE))
@@ -67,7 +67,19 @@ extern bool arm64_use_ng_mappings;#define PTE_MAYBE_NG (arm64_use_ng_mappings ? PTE_NG : 0)#define PMD_MAYBE_NG (arm64_use_ng_mappings ? PMD_SECT_NG : 0) +#ifndef CONFIG_ARM64_LPA2 #define lpa2_is_enabled () false +#define PTE_MAYBE_SHARED PTE_SHARED +#define PMD_MAYBE_SHARED PMD_SECT_S +#else +static inline bool __pure lpa2_is_enabled (void ) +{ +return read_tcr () &TCR_DS ; +} + +#define PTE_MAYBE_SHARED (lpa2_is_enabled() ? 0 : PTE_SHARED) +#define PMD_MAYBE_SHARED (lpa2_is_enabled() ? 0 : PMD_SECT_S) +#endif /* * If we have userspace only BTI we don't want to mark kernel pages
arch/arm64/mm/mmap.c +4 -0
@@ -73,6 +73,10 @@ static int __init adjust_protection_map(void) protection_map[VM_EXEC |VM_SHARED ] =PAGE_EXECONLY ; } +if (lpa2_is_enabled ()) +for (int i =0 ; i <ARRAY_SIZE (protection_map); i++) +pgprot_val (protection_map[i]) &= ~PTE_SHARED ; +return 0 ; }arch_initcall (adjust_protection_map);
arch/arm64/mm/proc.S +2 -0
@@ -466,6 +466,7 @@ alternative_else_nop_endif */ #define PTE_MAYBE_NG 0 +#define PTE_MAYBE_SHARED 0 mov_q x0, PIE_E0 msr REG_PIRE0_EL1, x0
@@ -473,6 +474,7 @@ alternative_else_nop_endif msr REG_PIR_EL1, x0 #undef PTE_MAYBE_NG +#undef PTE_MAYBE_SHARED mov x0, TCR2_EL1x_PIE msr REG_TCR2_EL1, x0
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 37/50 arm64: mm: Add LPA2 support to phys<->pte conversion routines
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
In preparation for enabling LPA2 support, introduce the mask values for converting between physical addresses and their representations in a page table descriptor.
While at it, move the pte_to_phys asm macro into its only user, so that we can freely modify it to use its input value register as a temp register.
For LPA2, the PTE_ADDR_MASK contains two non-adjacent sequences of zero bits, which means it no longer fits into the immediate field of an ordinary ALU instruction. So let's redefine it to include the bits in between as well, and only use it when converting from physical address to PTE representation, where the distinction does not matter. Also update the name accordingly to emphasize this.
--- arch/arm64/include/asm/assembler.h | 16 ++-------------- arch/arm64/include/asm/pgtable-hwdef.h | 10 +++++++--- arch/arm64/include/asm/pgtable.h | 5 +++-- arch/arm64/mm/proc.S | 8 ++++++++ 4 files changed, 20 insertions(+), 19 deletions(-)
arch/arm64/include/asm/assembler.h +2 -14
@@ -612,25 +612,13 @@ alternative_endif .macro phys_to_pte, pte, phys#ifdef CONFIG_ARM64_PA_BITS_52 -/* -* We assume \phys is 64K aligned and this is guaranteed by only -* supporting this configuration with 64K pages. -*/ - orr \pte, \phys, \phys, lsr #36 - and \pte, \pte, #PTE_ADDR_MASK + orr \pte, \phys, \phys, lsr #PTE_ADDR_HIGH_SHIFT + and \pte, \pte, #PHYS_TO_PTE_ADDR_MASK #else mov \pte, \phys #endif .endm - .macro pte_to_phys, phys, pte - and \phys, \pte, #PTE_ADDR_MASK -#ifdef CONFIG_ARM64_PA_BITS_52 - orr \phys, \phys, \phys, lsl #PTE_ADDR_HIGH_SHIFT - and \phys, \phys, GENMASK_ULL(PHYS_MASK_SHIFT - 1, PAGE_SHIFT) -#endif - .endm -/* * tcr_clear_errata_bits - Clear TCR bits that trigger an errata on this CPU. */
arch/arm64/include/asm/pgtable-hwdef.h +7 -3
@@ -155,13 +155,17 @@#define PTE_PXN (_AT(pteval_t, 1) << 53)/* Privileged XN */ #define PTE_UXN (_AT(pteval_t, 1) << 54)/* User XN */ -#define PTE_ADDR_LOW (((_AT(pteval_t, 1) << (48 - PAGE_SHIFT)) - 1) << PAGE_SHIFT) +#define PTE_ADDR_LOW (((_AT(pteval_t, 1) << (50 - PAGE_SHIFT)) - 1) << PAGE_SHIFT)#ifdef CONFIG_ARM64_PA_BITS_52 +#ifdef CONFIG_ARM64_64K_PAGES #define PTE_ADDR_HIGH (_AT(pteval_t, 0xf) << 12) -#define PTE_ADDR_MASK (PTE_ADDR_LOW | PTE_ADDR_HIGH)#define PTE_ADDR_HIGH_SHIFT 36 +#define PHYS_TO_PTE_ADDR_MASK (PTE_ADDR_LOW | PTE_ADDR_HIGH)#else -#define PTE_ADDR_MASK PTE_ADDR_LOW +#define PTE_ADDR_HIGH (_AT(pteval_t, 0x3) << 8) +#define PTE_ADDR_HIGH_SHIFT 42 +#define PHYS_TO_PTE_ADDR_MASK GENMASK_ULL(49, 8) +#endif #endif /*
arch/arm64/include/asm/pgtable.h +3 -2
@@ -80,15 +80,16 @@ extern unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)];#ifdef CONFIG_ARM64_PA_BITS_52 static inline phys_addr_t __pte_to_phys (pte_t pte) { +pte_val (pte) &= ~PTE_MAYBE_SHARED ;return (pte_val (pte) &PTE_ADDR_LOW ) | ((pte_val (pte) &PTE_ADDR_HIGH ) <<PTE_ADDR_HIGH_SHIFT ); }static inline pteval_t __phys_to_pte_val (phys_addr_t phys) { -return (phys | (phys >>PTE_ADDR_HIGH_SHIFT )) &PTE_ADDR_MASK ; +return (phys | (phys >>PTE_ADDR_HIGH_SHIFT )) &PHYS_TO_PTE_ADDR_MASK ; }#else -#define __pte_to_phys (pte) (pte_val(pte) & PTE_ADDR_MASK) +#define __pte_to_phys (pte) (pte_val(pte) & PTE_ADDR_LOW)#define __phys_to_pte_val (phys) (phys)#endif
arch/arm64/mm/proc.S +8 -0
@@ -205,6 +205,14 @@ SYM_FUNC_ALIAS(__pi_idmap_cpu_replace_ttbr1, idmap_cpu_replace_ttbr1) .pushsection ".idmap.text", "a" + .macro pte_to_phys, phys, pte + and \phys, \pte, #PTE_ADDR_LOW +#ifdef CONFIG_ARM64_PA_BITS_52 + and \pte, \pte, #PTE_ADDR_HIGH + orr \phys, \phys, \pte, lsl #PTE_ADDR_HIGH_SHIFT +#endif + .endm + .macro kpti_mk_tbl_ng, type, num_entries add end_\type\()p, cur_\type\()p, #\num_entries * 8 .Ldo_\type:
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 38/50 arm64: mm: Add definitions to support 5 levels of paging
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add the required types and descriptor accessors to support 5 levels of paging in the common code. This is one of the prerequisites for supporting 52-bit virtual addressing with 4k pages.
Note that this does not cover the code that handles kernel mappings or the fixmap.
--- arch/arm64/include/asm/pgalloc.h | 41 ++++++++++ arch/arm64/include/asm/pgtable-hwdef.h | 22 +++++- arch/arm64/include/asm/pgtable-types.h | 6 ++ arch/arm64/include/asm/pgtable.h | 82 +++++++++++++++++++- arch/arm64/mm/mmu.c | 31 +++++++- arch/arm64/mm/pgd.c | 15 +++- 6 files changed, 188 insertions(+), 9 deletions(-)
arch/arm64/include/asm/pgalloc.h +41 -0
@@ -60,6 +60,47 @@ static inline void __p4d_populate(p4d_t *p4dp, phys_addr_t pudp, p4dval_t prot) }#endif /* CONFIG_PGTABLE_LEVELS > 3 */ +#if CONFIG_PGTABLE_LEVELS >4 + +static inline void __pgd_populate (pgd_t *pgdp,phys_addr_t p4dp,pgdval_t prot) +{ +if (pgtable_l5_enabled ()) +set_pgd (pgdp,__pgd (__phys_to_pgd_val (p4dp) | prot)); +} + +static inline void pgd_populate (struct mm_struct *mm,pgd_t *pgdp,p4d_t *p4dp) +{ +pgdval_t pgdval =PGD_TYPE_TABLE ; + + pgdval |= (mm == &init_mm) ?PGD_TABLE_UXN :PGD_TABLE_PXN ; +__pgd_populate (pgdp,__pa (p4dp), pgdval); +} + +static inline p4d_t *p4d_alloc_one (struct mm_struct *mm,unsigned long addr) +{ +gfp_t gfp =GFP_PGTABLE_USER ; + +if (mm == &init_mm) + gfp =GFP_PGTABLE_KERNEL ; +return (p4d_t *)get_zeroed_page (gfp); +} + +static inline void p4d_free (struct mm_struct *mm,p4d_t *p4d) +{ +if (!pgtable_l5_enabled ()) +return ; +BUG_ON ((unsigned long )p4d & (PAGE_SIZE -1 )); +free_page ((unsigned long )p4d); +} + +#define __p4d_free_tlb (tlb, p4d, addr) p4d_free((tlb)->mm, p4d) +#else +static inline void __pgd_populate (pgd_t *pgdp,phys_addr_t p4dp,pgdval_t prot) +{ +BUILD_BUG (); +} +#endif /* CONFIG_PGTABLE_LEVELS > 4 */ +extern pgd_t *pgd_alloc (struct mm_struct *mm);extern void pgd_free (struct mm_struct *mm,pgd_t *pgdp);
arch/arm64/include/asm/pgtable-hwdef.h +19 -3
@@ -26,10 +26,10 @@#define ARM64_HW_PGTABLE_LEVELS (va_bits) (((va_bits) - 4) / (PAGE_SHIFT - 3))/* -* Size mapped by an entry at level n ( 0 <= n <= 3) +* Size mapped by an entry at level n ( -1 <= n <= 3) * We map (PAGE_SHIFT - 3) at all translation levels and PAGE_SHIFT bits * in the final page. The maximum number of translation levels supported by -* the architecture is 4. Hence, starting at level n, we have further +* the architecture is 5. Hence, starting at level n, we have further * ((4 - n) - 1) levels of translation excluding the offset within the page. * So, the total number of bits mapped by an entry at level n is : *
@@ -62,9 +62,16 @@#define PTRS_PER_PUD (1 << (PAGE_SHIFT - 3))#endif +#if CONFIG_PGTABLE_LEVELS >4 +#define P4D_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(0) +#define P4D_SIZE (_AC(1, UL) << P4D_SHIFT) +#define P4D_MASK (~(P4D_SIZE-1)) +#define PTRS_PER_P4D (1 << (PAGE_SHIFT - 3)) +#endif +/* * PGDIR_SHIFT determines the size a top-level page table entry can map -* (depending on the configuration, this level can be 0, 1 or 2). +* (depending on the configuration, this level can be -1, 0, 1 or 2). */ #define PGDIR_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS)#define PGDIR_SIZE (_AC(1, UL) << PGDIR_SHIFT)
@@ -87,6 +94,15 @@/* * Hardware page table definitions. * +* Level -1 descriptor (PGD). +*/ +#define PGD_TYPE_TABLE (_AT(pgdval_t, 3) << 0) +#define PGD_TABLE_BIT (_AT(pgdval_t, 1) << 1) +#define PGD_TYPE_MASK (_AT(pgdval_t, 3) << 0) +#define PGD_TABLE_PXN (_AT(pgdval_t, 1) << 59) +#define PGD_TABLE_UXN (_AT(pgdval_t, 1) << 60) + +/* * Level 0 descriptor (P4D). */ #define P4D_TYPE_TABLE (_AT(p4dval_t, 3) << 0)
arch/arm64/include/asm/pgtable-types.h +6 -0
@@ -36,6 +36,12 @@ typedef struct { pudval_t pud; } pud_t;#define __pud (x) ((pud_t) { (x) } )#endif +#if CONFIG_PGTABLE_LEVELS >4 +typedef struct {p4dval_t p4d; }p4d_t ; +#define p4d_val (x) ((x).p4d) +#define __p4d (x) ((p4d_t) { (x) } ) +#endif +typedef struct {pgdval_t pgd; }pgd_t ;#define pgd_val (x) ((x).pgd)#define __pgd (x) ((pgd_t) { (x) } )
arch/arm64/include/asm/pgtable.h +81 -1
94 lines not on this page · raw message
arch/arm64/mm/mmu.c +29 -2
@@ -1025,7 +1025,7 @@ static void free_empty_pud_table(p4d_t *p4dp, unsigned long addr,if (CONFIG_PGTABLE_LEVELS <=3 )return ; -if (!pgtable_range_aligned (start, end, floor, ceiling,PGDIR_MASK )) +if (!pgtable_range_aligned (start, end, floor, ceiling,P4D_MASK ))return ;/*
@@ -1048,8 +1048,8 @@ static void free_empty_p4d_table(pgd_t *pgdp, unsigned long addr, unsigned long end, unsigned long floor, unsigned long ceiling) { - unsigned long next; p4d_t *p4dp, p4d; +unsigned long i, next, start = addr; do { next = p4d_addr_end(addr, end);
@@ -1061,6 +1061,27 @@ static void free_empty_p4d_table(pgd_t *pgdp, unsigned long addr,WARN_ON (!p4d_present (p4d));free_empty_pud_table (p4dp, addr, next, floor, ceiling); }while (addr = next, addr < end); + +if (!pgtable_l5_enabled ()) +return ; + +if (!pgtable_range_aligned (start, end, floor, ceiling,PGDIR_MASK )) +return ; + +/* +* Check whether we can free the p4d page if the rest of the +* entries are empty. Overlap with other regions have been +* handled by the floor/ceiling check. +*/ + p4dp =p4d_offset (pgdp,0UL ); +for (i =0 ; i <PTRS_PER_P4D ; i++) { +if (!p4d_none (READ_ONCE (p4dp[i]))) +return ; + } + +pgd_clear (pgdp); +__flush_tlb_kernel_pgtable (start); +free_hotplug_pgtable_page (virt_to_page (p4dp)); } static void free_empty_tables(unsigned long addr, unsigned long end,
@@ -1145,6 +1166,12 @@ int pmd_set_huge(pmd_t *pmdp, phys_addr_t phys, pgprot_t prot)return 1 ; } +#ifndef __PAGETABLE_P4D_FOLDED +void p4d_clear_huge (p4d_t *p4dp) +{ +} +#endif +int pud_clear_huge (pud_t *pudp) {if (!pud_sect (READ_ONCE (*pudp)))
arch/arm64/mm/pgd.c +12 -3
@@ -17,11 +17,20 @@static struct kmem_cache *pgd_cache__ro_after_init ; +static bool pgdir_is_page_size (void ) +{ +if (PGD_SIZE ==PAGE_SIZE ) +return true; +if (CONFIG_PGTABLE_LEVELS ==5 ) +return !pgtable_l5_enabled (); +return false; +} +pgd_t *pgd_alloc (struct mm_struct *mm) {gfp_t gfp =GFP_PGTABLE_USER ; -if (PGD_SIZE ==PAGE_SIZE ) +if (pgdir_is_page_size ())return (pgd_t *)__get_free_page (gfp);else return kmem_cache_alloc (pgd_cache, gfp);
@@ -29,7 +38,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)void pgd_free (struct mm_struct *mm,pgd_t *pgd) { -if (PGD_SIZE ==PAGE_SIZE ) +if (pgdir_is_page_size ())free_page ((unsigned long )pgd);else kmem_cache_free (pgd_cache, pgd);
@@ -37,7 +46,7 @@ void pgd_free(struct mm_struct *mm, pgd_t *pgd)void __init pgtable_cache_init (void ) { -if (PGD_SIZE ==PAGE_SIZE ) +if (pgdir_is_page_size ())return ;#ifdef CONFIG_ARM64_PA_BITS_52
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 39/50 arm64: mm: add LPA2 and 5 level paging support to G-to-nG conversion
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add support for 5 level paging in the G-to-nG routine that creates its own temporary page tables to traverse the swapper page tables. Also add support for running the 5 level configuration with the top level folded at runtime, to support CPUs that do not implement the LPA2 extension.
While at it, wire up the level skipping logic so it will also trigger on 4 level configurations with LPA2 enabled at build time but not active at runtime, as we'll fall back to 3 level paging in that case.
--- arch/arm64/kernel/cpufeature.c | 9 ++- arch/arm64/mm/proc.S | 70 +++++++++++++++++--- 2 files changed, 66 insertions(+), 13 deletions(-)
arch/arm64/kernel/cpufeature.c +6 -3
@@ -1765,6 +1765,9 @@ static int __init __kpti_install_ng_mappings(void *__unused)pgd_t *kpti_ng_temp_pgd;u64 alloc =0 ; +if (levels ==5 && !pgtable_l5_enabled ()) + levels =4 ; + remap_fn = (void *)__pa_symbol (idmap_kpti_install_ng_mappings);if (!cpu) {
@@ -1778,9 +1781,9 @@ static int __init __kpti_install_ng_mappings(void *__unused)// // The physical pages are laid out as follows: // -// +--------+-/-------+-/------ +-\\--------+ -// : PTE[] : | PMD[] : | PUD[] : || PGD[] : -// +--------+-\-------+-\------ +-//--------+ +// +--------+-/-------+-/------ +-/------ +-\\\--------+ +// : PTE[] : | PMD[] : | PUD[] : | P4D[] : ||| PGD[] : +// +--------+-\-------+-\------ +-\------ +-///--------+ // ^ // The first page is mapped into this hierarchy at a PMD_SHIFT // aligned virtual address, so that we can manipulate the PTE
arch/arm64/mm/proc.S +60 -10
137 lines not on this page · raw message
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 40/50 arm64: Enable LPA2 at boot if supported by the system
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Update the early kernel mapping code to take 52-bit virtual addressing into account based on the LPA2 feature. This is a bit more involved than LVA (which is supported with 64k pages only), given that some page table descriptor bits change meaning in this case.
To keep the handling in asm to a minimum, the initial ID map is still created with 48-bit virtual addressing, which implies that the kernel image must be loaded into 48-bit addressable physical memory. This is currently required by the boot protocol, even though we happen to support placement outside of that for LVA/64k based configurations.
Enabling LPA2 involves more than setting TCR.T1SZ to a lower value, there is also a DS bit in TCR that needs to be set, and which changes the meaning of bits [9:8] in all page table descriptors. Since we cannot enable DS and every live page table descriptor at the same time, let's pivot through another temporary mapping. This avoids the need to reintroduce manipulations of the page tables with the MMU and caches disabled.
To permit the LPA2 feature to be overridden on the kernel command line, which may be necessary to work around silicon errata, or to deal with mismatched features on heterogeneous SoC designs, test for CPU feature overrides first, and only then enable LPA2.
--- arch/arm64/include/asm/assembler.h | 8 ++- arch/arm64/include/asm/cpufeature.h | 18 +++++ arch/arm64/include/asm/memory.h | 4 ++ arch/arm64/kernel/head.S | 8 +++ arch/arm64/kernel/image-vars.h | 1 + arch/arm64/kernel/pi/map_kernel.c | 70 +++++++++++++++++++- arch/arm64/kernel/pi/map_range.c | 11 ++- arch/arm64/kernel/pi/pi.h | 4 +- arch/arm64/mm/init.c | 2 +- arch/arm64/mm/mmu.c | 6 +- arch/arm64/mm/proc.S | 3 + 11 files changed, 124 insertions(+), 11 deletions(-)
arch/arm64/include/asm/assembler.h +7 -1
@@ -581,11 +581,17 @@ alternative_endif* but we have to add an offset so that the TTBR1 address corresponds with the * pgdir entry that covers the lowest 48-bit addressable VA. * +* Note that this trick is only used for LVA/64k pages - LPA2/4k pages uses an +* additional paging level, and on LPA2/16k pages, we would end up with a root +* level table with only 2 entries, which is suboptimal in terms of TLB +* utilization, so there we fall back to 47 bits of translation if LPA2 is not +* supported. +* * orr is used as it can cover the immediate value (and is idempotent). * ttbr: Value of ttbr to set, modified. */ .macro offset_ttbr1, ttbr, tmp -#ifdef CONFIG_ARM64_VA_BITS_52 +#if defined(CONFIG_ARM64_VA_BITS_52 ) && !defined(CONFIG_ARM64_LPA2 ) mrs \tmp, tcr_el1 and \tmp, \tmp, #TCR_T1SZ_MASK cmp \tmp, #TCR_T1SZ(VA_BITS_MIN)
arch/arm64/include/asm/cpufeature.h +18 -0
@@ -1008,6 +1008,24 @@ static inline bool cpu_has_lva(void) ID_AA64MMFR2_EL1_VARange_SHIFT); } +static inline bool cpu_has_lpa2 (void ) +{ +#ifdef CONFIG_ARM64_LPA2 +u64 mmfr0; +int feat; + + mmfr0 =read_sysreg (id_aa64mmfr0_el1); + mmfr0 &= ~id_aa64mmfr0_override.mask; + mmfr0 |= id_aa64mmfr0_override.val; + feat =cpuid_feature_extract_signed_field (mmfr0, +ID_AA64MMFR0_EL1_TGRAN_SHIFT ); + +return feat >=ID_AA64MMFR0_EL1_TGRAN_LPA2 ; +#else +return false; +#endif +} +#endif /* __ASSEMBLY__ */ #endif
arch/arm64/include/asm/memory.h +4 -0
@@ -54,7 +54,11 @@#define FIXADDR_TOP (-UL(SZ_8M))#if VA_BITS >48 +#ifdef CONFIG_ARM64_16K_PAGES +#define VA_BITS_MIN (47) +#else #define VA_BITS_MIN (48) +#endif #else #define VA_BITS_MIN (VA_BITS)#endif
arch/arm64/kernel/head.S +8 -0
@@ -89,6 +89,7 @@ SYM_CODE_START(primary_entry) mov sp, x1 mov x29, xzr adrp x0, init_idmap_pg_dir + mov x1, xzr bl __pi_create_init_idmap /*
@@ -473,9 +474,16 @@ SYM_FUNC_END(__enable_mmu) #ifdef CONFIG_ARM64_VA_BITS_52 SYM_FUNC_START(__cpu_secondary_check52bitva) +#ifndef CONFIG_ARM64_LPA2 mrs_s x0, SYS_ID_AA64MMFR2_EL1 and x0, x0, ID_AA64MMFR2_EL1_VARange_MASK cbnz x0, 2f +#else + mrs x0, id_aa64mmfr0_el1 + sbfx x0, x0, #ID_AA64MMFR0_EL1_TGRAN_SHIFT, 4 + cmp x0, #ID_AA64MMFR0_EL1_TGRAN_LPA2 + b.ge 2f +#endif update_early_cpu_boot_status \ CPU_STUCK_IN_KERNEL | CPU_STUCK_REASON_52_BIT_VA, x0, x1
arch/arm64/kernel/image-vars.h +1 -0
@@ -54,6 +54,7 @@ PROVIDE(__pi__ctype = _ctype);PROVIDE (__pi_memstart_offset_seed = memstart_offset_seed);PROVIDE (__pi_init_idmap_pg_dir = init_idmap_pg_dir); +PROVIDE (__pi_init_idmap_pg_end = init_idmap_pg_end);PROVIDE (__pi_init_pg_dir = init_pg_dir);PROVIDE (__pi_init_pg_end = init_pg_end);PROVIDE (__pi_swapper_pg_dir = swapper_pg_dir);
arch/arm64/kernel/pi/map_kernel.c +67 -3
98 lines not on this page · raw message
arch/arm64/kernel/pi/map_range.c +8 -3
@@ -87,14 +87,19 @@ void __init map_range(u64 *pte, u64 start, u64 end, u64 pa, pgprot_t prot, } } -asmlinkage u64 __init create_init_idmap(pgd_t *pg_dir) +asmlinkage u64 __init create_init_idmap(pgd_t *pg_dir, pteval_t clrmask) {u64 ptep = (u64 )pg_dir +PAGE_SIZE ; +pgprot_t text_prot =PAGE_KERNEL_ROX ; +pgprot_t data_prot =PAGE_KERNEL ; + +pgprot_val (text_prot) &= ~clrmask; +pgprot_val (data_prot) &= ~clrmask;map_range (&ptep, (u64 )_stext, (u64 )__initdata_begin, (u64 )_stext, -PAGE_KERNEL_ROX ,IDMAP_ROOT_LEVEL , (pte_t *)pg_dir, false,0 ); + text_prot,IDMAP_ROOT_LEVEL , (pte_t *)pg_dir, false,0 );map_range (&ptep, (u64 )__initdata_begin, (u64 )_end, (u64 )__initdata_begin, -PAGE_KERNEL ,IDMAP_ROOT_LEVEL , (pte_t *)pg_dir, false,0 ); + data_prot,IDMAP_ROOT_LEVEL , (pte_t *)pg_dir, false,0 );return ptep; }
arch/arm64/kernel/pi/pi.h +2 -2
@@ -21,7 +21,7 @@ static inline void *prel64_to_pointer(const prel64_t *offset)extern bool dynamic_scs_is_enabled; -extern pgd_t init_idmap_pg_dir[]; +extern pgd_t init_idmap_pg_dir[], init_idmap_pg_end[];void init_feature_override (u64 boot_status,const void *fdt,int chosen);u64 kaslr_early_init (void *fdt,int chosen);
@@ -33,4 +33,4 @@ void map_range(u64 *pgd, u64 start, u64 end, u64 pa, pgprot_t prot, asmlinkage void early_map_kernel(u64 boot_status, void *fdt); -asmlinkage u64 create_init_idmap(pgd_t *pgd); +asmlinkage u64 create_init_idmap(pgd_t *pgd, pteval_t clrmask);
arch/arm64/mm/init.c +1 -1
@@ -238,7 +238,7 @@ void __init arm64_memblock_init(void)* physical address of PAGE_OFFSET, we have to *subtract* from it. */ if (IS_ENABLED (CONFIG_ARM64_VA_BITS_52 ) && (vabits_actual !=52 )) - memstart_addr -=_PAGE_OFFSET (48 ) -_PAGE_OFFSET (52 ); + memstart_addr -=_PAGE_OFFSET (vabits_actual) -_PAGE_OFFSET (52 );/* * Apply the memory limit if it was set. Since the kernel may be loaded
arch/arm64/mm/mmu.c +5 -1
@@ -582,8 +582,12 @@ static void __init map_mem(pgd_t *pgdp)* entries at any level are being shared between the linear region and * the vmalloc region. Check whether this is true for the PGD level, in * which case it is guaranteed to be true for all other levels as well. +* (Unless we are running with support for LPA2, in which case the +* entire reduced VA space is covered by a single pgd_t which will have +* been populated without the PXNTable attribute by the time we get here.) */ -BUILD_BUG_ON (pgd_index (direct_map_end -1 ) ==pgd_index (direct_map_end)); +BUILD_BUG_ON (pgd_index (direct_map_end -1 ) ==pgd_index (direct_map_end) && +pgd_index (_PAGE_OFFSET (VA_BITS_MIN )) !=PTRS_PER_PGD -1 ); early_kfence_pool =arm64_kfence_alloc_pool ();
arch/arm64/mm/proc.S +3 -0
@@ -488,6 +488,9 @@ SYM_FUNC_START(__cpu_setup) mov x9, #64 - VA_BITS alternative_if ARM64_HAS_VA52 tcr_set_t1sz tcr, x9 +#ifdef CONFIG_ARM64_LPA2 + orr tcr, tcr, #TCR_DS +#endif alternative_else_nop_endif #endif
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 41/50 arm64: mm: Add 5 level paging support to fixmap and swapper handling
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add support for using 5 levels of paging in the fixmap, as well as in the kernel page table handling code which uses fixmaps internally. This also handles the case where a 5 level build runs on hardware that only supports 4 levels of paging.
--- arch/arm64/include/asm/fixmap.h | 1 + arch/arm64/include/asm/pgtable.h | 45 ++++++++++++++++--- arch/arm64/mm/fixmap.c | 2 +- arch/arm64/mm/mmu.c | 47 ++++++++++++++++++-- 4 files changed, 85 insertions(+), 10 deletions(-)
arch/arm64/include/asm/fixmap.h +1 -0
@@ -87,6 +87,7 @@ enum fixed_addresses {FIX_PTE ,FIX_PMD ,FIX_PUD , +FIX_P4D ,FIX_PGD , __end_of_fixed_addresses
arch/arm64/include/asm/pgtable.h +40 -5
64 lines not on this page · raw message
arch/arm64/mm/fixmap.c +1 -1
@@ -104,7 +104,7 @@ void __init early_fixmap_init(void)unsigned long end =FIXADDR_TOP ;pgd_t *pgdp =pgd_offset_k (addr); -p4d_t *p4dp =p4d_offset (pgdp, addr); +p4d_t *p4dp =p4d_offset_kimg (pgdp, addr);early_fixmap_init_pud (p4dp, addr, end); }
arch/arm64/mm/mmu.c +43 -4
71 lines not on this page · raw message
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 42/50 arm64: kasan: Reduce minimum shadow alignment and enable 5 level paging
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Allow the KASAN init code to deal with 5 levels of paging, and relax the requirement that the shadow region is aligned to the top level pgd_t size. This is necessary for LPA2 based 52-bit virtual addressing, where the KASAN shadow will never be aligned to the pgd_t size. Allowing this also enables the 16k/48-bit case for KASAN, which is a nice bonus.
This involves some hackery to manipulate the root and next level page tables without having to distinguish all the various configurations, including 16k/48-bits (which has a two entry pgd_t level), and LPA2 configurations running with one translation level less on non-LPA2 hardware.
--- arch/arm64/Kconfig | 2 +- arch/arm64/mm/kasan_init.c | 148 +++++++++++++++++--- 2 files changed, 130 insertions(+), 20 deletions(-)
arch/arm64/Kconfig +1 -1
@@ -164,7 +164,7 @@ config ARM64 select HAVE_ARCH_HUGE_VMAP select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL_RELATIVE - select HAVE_ARCH_KASAN if !(ARM64_16K_PAGES && ARM64_VA_BITS_48) + select HAVE_ARCH_KASAN select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN select HAVE_ARCH_KASAN_SW_TAGS if HAVE_ARCH_KASAN select HAVE_ARCH_KASAN_HW_TAGS if (HAVE_ARCH_KASAN && ARM64_MTE)
arch/arm64/mm/kasan_init.c +129 -19
203 lines not on this page · raw message
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 43/50 arm64: mm: Add support for folding PUDs at runtime
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
In order to support LPA2 on 16k pages in a way that permits non-LPA2 systems to run the same kernel image, we have to be able to fall back to at most 48 bits of virtual addressing.
Falling back to 48 bits would result in a level 0 with only 2 entries, which is suboptimal in terms of TLB utilization. So instead, let's fall back to 47 bits in that case. This means we need to be able to fold PUDs dynamically, similar to how we fold P4Ds for 48 bit virtual addressing on LPA2 with 4k pages.
--- arch/arm64/include/asm/pgalloc.h | 12 ++- arch/arm64/include/asm/pgtable.h | 87 +++++++++++++++++--- arch/arm64/include/asm/tlb.h | 3 + arch/arm64/kernel/cpufeature.c | 2 + arch/arm64/mm/mmu.c | 2 +- arch/arm64/mm/pgd.c | 2 + 6 files changed, 95 insertions(+), 13 deletions(-)
arch/arm64/include/asm/pgalloc.h +11 -1
@@ -14,6 +14,7 @@#include <asm/tlbflush.h> #define __HAVE_ARCH_PGD_FREE +#define __HAVE_ARCH_PUD_FREE#include <asm-generic/pgalloc.h> #define PGD_SIZE (PTRS_PER_PGD * sizeof(pgd_t))
@@ -43,7 +44,8 @@ static inline void __pud_populate(pud_t *pudp, phys_addr_t pmdp, pudval_t prot)static inline void __p4d_populate (p4d_t *p4dp,phys_addr_t pudp,p4dval_t prot) { -set_p4d (p4dp,__p4d (__phys_to_p4d_val (pudp) | prot)); +if (pgtable_l4_enabled ()) +set_p4d (p4dp,__p4d (__phys_to_p4d_val (pudp) | prot)); }static inline void p4d_populate (struct mm_struct *mm,p4d_t *p4dp,pud_t *pudp)
@@ -53,6 +55,14 @@ static inline void p4d_populate(struct mm_struct *mm, p4d_t *p4dp, pud_t *pudp) p4dval |= (mm == &init_mm) ?P4D_TABLE_UXN :P4D_TABLE_PXN ; __p4d_populate(p4dp, __pa(pudp), p4dval); } + +static inline void pud_free (struct mm_struct *mm,pud_t *pud) +{ +if (!pgtable_l4_enabled ()) +return ; +BUG_ON ((unsigned long )pud & (PAGE_SIZE -1 )); +free_page ((unsigned long )pud); +}#else static inline void __p4d_populate (p4d_t *p4dp,phys_addr_t pudp,p4dval_t prot) {
arch/arm64/include/asm/pgtable.h +76 -11
120 lines not on this page · raw message
arch/arm64/include/asm/tlb.h +3 -0
@@ -103,6 +103,9 @@ static inline void __pud_free_tlb(struct mmu_gather *tlb, pud_t *pudp, {struct ptdesc *ptdesc =virt_to_ptdesc (pudp); +if (!pgtable_l4_enabled ()) +return ; +pagetable_pud_dtor (ptdesc);tlb_remove_ptdesc (tlb, ptdesc); }
arch/arm64/kernel/cpufeature.c +2 -0
@@ -1767,6 +1767,8 @@ static int __init __kpti_install_ng_mappings(void *__unused)if (levels ==5 && !pgtable_l5_enabled ()) levels =4 ; +else if (levels ==4 && !pgtable_l4_enabled ()) + levels =3 ; remap_fn = (void *)__pa_symbol (idmap_kpti_install_ng_mappings);
arch/arm64/mm/mmu.c +1 -1
@@ -1065,7 +1065,7 @@ static void free_empty_pud_table(p4d_t *p4dp, unsigned long addr,free_empty_pmd_table (pudp, addr, next, floor, ceiling); }while (addr = next, addr < end); -if (CONFIG_PGTABLE_LEVELS <=3 ) +if (!pgtable_l4_enabled ())return ;if (!pgtable_range_aligned (start, end, floor, ceiling,P4D_MASK ))
arch/arm64/mm/pgd.c +2 -0
@@ -21,6 +21,8 @@ static bool pgdir_is_page_size(void) {if (PGD_SIZE ==PAGE_SIZE )return true; +if (CONFIG_PGTABLE_LEVELS ==4 ) +return !pgtable_l4_enabled ();if (CONFIG_PGTABLE_LEVELS ==5 )return !pgtable_l5_enabled ();return false;
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 44/50 arm64: ptdump: Disregard unaddressable VA space
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Configurations built with support for 52-bit virtual addressing can also run on CPUs that only support 48 bits of VA space, in which case only that part of swapper_pg_dir that represents the 48-bit addressable region is relevant, and everything else is ignored by the hardware.
Our software pagetable walker has little in the way of input address validation, and so it will happily start a walk from an address that is not representable by the number of paging levels that are actually active, resulting in lots of bogus output from the page table dumper unless we take care to start at a valid address.
So define the start address at runtime based on vabits_actual.
--- arch/arm64/mm/ptdump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arch/arm64/mm/ptdump.c +2 -2
@@ -313,7 +313,6 @@ static void __init ptdump_initialize(void)static struct ptdump_info kernel_ptdump_info__ro_after_init = { .mm = &init_mm, - .base_addr =PAGE_OFFSET , };void ptdump_check_wx (void )
@@ -329,7 +328,7 @@ void ptdump_check_wx(void) .ptdump = { .note_page = note_page, .range = (struct ptdump_range []) { - {PAGE_OFFSET , ~0UL }, + {_PAGE_OFFSET (vabits_actual), ~0UL }, {0 ,0 } } }
@@ -370,6 +369,7 @@ static int __init ptdump_init(void)static struct addr_marker address_markers[ARRAY_SIZE (m)]__ro_after_init ; kernel_ptdump_info.markers =memcpy (address_markers, m,sizeof (m)); + kernel_ptdump_info.base_addr = page_offset;ptdump_initialize ();ptdump_debugfs_register (&kernel_ptdump_info,"kernel_page_tables" );
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 45/50 arm64: ptdump: Deal with translation levels folded at runtime
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Currently, the ptdump code deals with folded PMD or PUD levels at build time, by omitting those levels when invoking note_page. IOW, note_page() is never invoked with level == 1 if P4Ds are folded in the build configuration.
With the introduction of LPA2 support, we will defer some of these folding decisions to runtime, so let's take care of this by overriding the 'level' argument when this condition triggers.
Substituting the PUD or PMD strings for "PGD" when the level in question is folded at build time is no longer necessary, and so the conditional expressions can be simplified. This also makes the indirection of the 'name' field unnecessary, so change that into a char[] array, and make the whole thing __ro_after_init.
Note that the mm_p?d_folded() functions currently ignore their mm pointer arguments, but let's wire them up correctly anyway.
--- arch/arm64/mm/ptdump.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-)
arch/arm64/mm/ptdump.c +12 -5
@@ -48,6 +48,7 @@ struct pg_state {struct ptdump_state ptdump;struct seq_file *seq;const struct addr_marker *marker; +const struct mm_struct *mm;unsigned long start_address;int level;u64 current_prot;
@@ -144,12 +145,12 @@ static const struct prot_bits pte_bits[] = {struct pg_level {const struct prot_bits *bits; -const char *name; -size_t num; +char name[4 ]; +int num;u64 mask; }; -static struct pg_level pg_level[] = { +static struct pg_level pg_level[]__ro_after_init = { {/* pgd */ .name ="PGD" , .bits = pte_bits,
@@ -159,11 +160,11 @@ static struct pg_level pg_level[] = { .bits = pte_bits, .num = ARRAY_SIZE(pte_bits), }, { /* pud */ - .name = (CONFIG_PGTABLE_LEVELS > 3) ? "PUD" : "PGD", + .name = "PUD", .bits = pte_bits, .num = ARRAY_SIZE(pte_bits), }, { /* pmd */ - .name = (CONFIG_PGTABLE_LEVELS > 2) ? "PMD" : "PGD", + .name = "PMD", .bits = pte_bits, .num = ARRAY_SIZE(pte_bits), }, { /* pte */
@@ -227,6 +228,11 @@ static void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,static const char units[] ="KMGTPE" ;u64 prot =0 ; +/* check if the current level has been folded dynamically */ +if ((level ==1 &&mm_p4d_folded (st->mm)) || + (level ==2 &&mm_pud_folded (st->mm))) + level =0 ; +if (level >=0 ) prot = val & pg_level[level].mask;
@@ -288,6 +294,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info) st = (struct pg_state ){ .seq = s, .marker = info->markers, + .mm = info->mm, .level =-1 , .ptdump = { .note_page = note_page,
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 46/50 arm64: Enable 52-bit virtual addressing for 4k and 16k granule configs
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Update Kconfig to permit 4k and 16k granule configurations to be built with 52-bit virtual addressing, now that all the prerequisites are in place.
While at it, update the feature description so it matches on the appropriate feature bits depending on the page size. For simplicity, let's just keep ARM64_HAS_VA52 as the feature name.
Note that LPA2 based 52-bit virtual addressing requires 52-bit physical addressing support to be enabled as well, as programming TCR.TxSZ to values below 16 is not allowed unless TCR.DS is set, which is what activates the 52-bit physical addressing support.
While supporting the converse (52-bit physical addressing without 52-bit virtual addressing) would be possible in principle, let's keep things simple, by only allowing these features to be enabled at the same time.
--- arch/arm64/Kconfig | 17 ++++++++------- arch/arm64/kernel/cpufeature.c | 22 ++++++++++++++++---- 2 files changed, 28 insertions(+), 11 deletions(-)
arch/arm64/Kconfig +10 -7
@@ -368,7 +368,9 @@ config PGTABLE_LEVELS default 3 if ARM64_64K_PAGES && (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) default 3 if ARM64_4K_PAGES && ARM64_VA_BITS_39 default 3 if ARM64_16K_PAGES && ARM64_VA_BITS_47 + default 4 if ARM64_16K_PAGES && (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) default 4 if !ARM64_64K_PAGES && ARM64_VA_BITS_48 + default 5 if ARM64_4K_PAGES && ARM64_VA_BITS_52 config ARCH_SUPPORTS_UPROBES def_bool y
@@ -396,13 +398,13 @@ config BUILTIN_RETURN_ADDRESS_STRIPS_PAC config KASAN_SHADOW_OFFSET hex depends on KASAN_GENERIC || KASAN_SW_TAGS - default 0xdfff800000000000 if (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) && !KASAN_SW_TAGS - default 0xdfffc00000000000 if ARM64_VA_BITS_47 && !KASAN_SW_TAGS + default 0xdfff800000000000 if (ARM64_VA_BITS_48 || (ARM64_VA_BITS_52 && !ARM64_16K_PAGES)) && !KASAN_SW_TAGS + default 0xdfffc00000000000 if (ARM64_VA_BITS_47 || ARM64_VA_BITS_52) && ARM64_16K_PAGES && !KASAN_SW_TAGS default 0xdffffe0000000000 if ARM64_VA_BITS_42 && !KASAN_SW_TAGS default 0xdfffffc000000000 if ARM64_VA_BITS_39 && !KASAN_SW_TAGS default 0xdffffff800000000 if ARM64_VA_BITS_36 && !KASAN_SW_TAGS - default 0xefff800000000000 if (ARM64_VA_BITS_48 || ARM64_VA_BITS_52) && KASAN_SW_TAGS - default 0xefffc00000000000 if ARM64_VA_BITS_47 && KASAN_SW_TAGS + default 0xefff800000000000 if (ARM64_VA_BITS_48 || (ARM64_VA_BITS_52 && !ARM64_16K_PAGES)) && KASAN_SW_TAGS + default 0xefffc00000000000 if (ARM64_VA_BITS_47 || ARM64_VA_BITS_52) && ARM64_16K_PAGES && KASAN_SW_TAGS default 0xeffffe0000000000 if ARM64_VA_BITS_42 && KASAN_SW_TAGS default 0xefffffc000000000 if ARM64_VA_BITS_39 && KASAN_SW_TAGS default 0xeffffff800000000 if ARM64_VA_BITS_36 && KASAN_SW_TAGS
@@ -1310,7 +1312,7 @@ config ARM64_VA_BITS_48 config ARM64_VA_BITS_52 bool "52-bit" - depends on ARM64_64K_PAGES && (ARM64_PAN || !ARM64_SW_TTBR0_PAN) + depends on ARM64_PAN || !ARM64_SW_TTBR0_PAN help Enable 52-bit virtual addressing for userspace when explicitly requested via a hint to mmap(). The kernel will also use 52-bit
@@ -1357,10 +1359,11 @@ choice config ARM64_PA_BITS_48 bool "48-bit" + depends on ARM64_64K_PAGES || !ARM64_VA_BITS_52 config ARM64_PA_BITS_52 - bool "52-bit (ARMv8.2)" - depends on ARM64_64K_PAGES + bool "52-bit" + depends on ARM64_64K_PAGES || ARM64_VA_BITS_52 depends on ARM64_PAN || !ARM64_SW_TTBR0_PAN help Enable support for a 52-bit physical address space, introduced as
arch/arm64/kernel/cpufeature.c +18 -4
@@ -2703,15 +2703,29 @@ static const struct arm64_cpu_capabilities arm64_features[] = { }, #ifdef CONFIG_ARM64_VA_BITS_52 { - .desc = "52-bit Virtual Addressing (LVA)", .capability = ARM64_HAS_VA52, .type = ARM64_CPUCAP_BOOT_CPU_FEATURE, - .sys_reg = SYS_ID_AA64MMFR2_EL1, - .sign = FTR_UNSIGNED, + .matches = has_cpuid_feature, .field_width = 4, +#ifdef CONFIG_ARM64_64K_PAGES + .desc = "52-bit Virtual Addressing (LVA)", + .sign = FTR_SIGNED, + .sys_reg = SYS_ID_AA64MMFR2_EL1, .field_pos = ID_AA64MMFR2_EL1_VARange_SHIFT, - .matches = has_cpuid_feature, .min_field_value = ID_AA64MMFR2_EL1_VARange_52, +#else + .desc = "52-bit Virtual Addressing (LPA2)", + .sys_reg = SYS_ID_AA64MMFR0_EL1, +#ifdef CONFIG_ARM64_4K_PAGES + .sign = FTR_SIGNED, + .field_pos = ID_AA64MMFR0_EL1_TGRAN4_SHIFT, + .min_field_value = ID_AA64MMFR0_EL1_TGRAN4_52_BIT, +#else + .sign = FTR_UNSIGNED, + .field_pos = ID_AA64MMFR0_EL1_TGRAN16_SHIFT, + .min_field_value = ID_AA64MMFR0_EL1_TGRAN16_52_BIT, +#endif +#endif }, #endif {},
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 47/50 arm64: defconfig: Enable LPA2 support
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
We typically enable support in defconfig for all architectural features for which we can detect at runtime if the hardware actually supports them.
Now that we have implemented support for LPA2 based 52-bit virtual addressing in a way that should not impact 48-bit operation on non-LPA2 CPU, we can do the same, and enable 52-bit virtual addressing by default.
--- arch/arm64/configs/defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/arm64/configs/defconfig +1 -1
@@ -76,7 +76,7 @@ CONFIG_ARCH_VEXPRESS=y CONFIG_ARCH_VISCONTI=y CONFIG_ARCH_XGENE=y CONFIG_ARCH_ZYNQMP=y -CONFIG_ARM64_VA_BITS_48=y +CONFIG_ARM64_VA_BITS_52=y CONFIG_SCHED_MC=y CONFIG_SCHED_SMT=y CONFIG_NUMA=y
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 48/50 mm: add arch hook to validate mmap() prot flags
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
Add a hook to permit architectures to perform validation on the prot flags passed to mmap(), like arch_validate_prot() does for mprotect(). This will be used by arm64 to reject PROT_WRITE+PROT_EXEC mappings on configurations that run with WXN enabled.
--- include/linux/mman.h | 15 +++++++++++++++ mm/mmap.c | 3 +++ 2 files changed, 18 insertions(+)
include/linux/mman.h +15 -0
@@ -124,6 +124,21 @@ static inline bool arch_validate_flags(unsigned long flags)#define arch_validate_flags arch_validate_flags#endif +#ifndef arch_validate_mmap_prot +/* +* This is called from mmap(), which ignores unknown prot bits so the default +* is to accept anything. +* +* Returns true if the prot flags are valid +*/ +static inline bool arch_validate_mmap_prot (unsigned long prot, +unsigned long addr) +{ +return true; +} +#define arch_validate_mmap_prot arch_validate_mmap_prot +#endif +/* * Optimisation macro. It is equivalent to: * (x & bit1) ? bit2 : 0
mm/mmap.c +3 -0
@@ -1229,6 +1229,9 @@ unsigned long do_mmap(struct file *file, unsigned long addr,if (!(file &&path_noexec (&file->f_path))) prot |=PROT_EXEC ; +if (!arch_validate_mmap_prot (prot, addr)) +return -EACCES ; +/* force arch specific MAP_FIXED handling in get_unmapped_area */ if (flags &MAP_FIXED_NOREPLACE ) flags |=MAP_FIXED ;
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 49/50 arm64: mm: add support for WXN memory translation attribute
In reply to Ard BiesheuvelFrom: Ard Biesheuvel <[email protected]>
The AArch64 virtual memory system supports a global WXN control, which can be enabled to make all writable mappings implicitly no-exec. This is a useful hardening feature, as it prevents mistakes in managing page table permissions from being exploited to attack the system.
When enabled at EL1, the restrictions apply to both EL1 and EL0. EL1 is completely under our control, and has been cleaned up to allow WXN to be enabled from boot onwards. EL0 is not under our control, but given that widely deployed security features such as selinux or PaX already limit the ability of user space to create mappings that are writable and executable at the same time, the impact of enabling this for EL0 is expected to be limited. (For this reason, common user space libraries that have a legitimate need for manipulating executable code already carry fallbacks such as [0].)
If enabled at compile time, the feature can still be disabled at boot if needed, by passing arm64.nowxn on the kernel command line.
[0] https://github.com/libffi/libffi/blob/master/src/closures.c#L440
--- arch/arm64/Kconfig | 11 ++++++ arch/arm64/include/asm/cpufeature.h | 8 +++++ arch/arm64/include/asm/mman.h | 36 ++++++++++++++++++++ arch/arm64/include/asm/mmu_context.h | 30 +++++++++++++++- arch/arm64/kernel/pi/idreg-override.c | 4 ++- arch/arm64/kernel/pi/map_kernel.c | 23 +++++++++++++ arch/arm64/mm/proc.S | 6 ++++ 7 files changed, 116 insertions(+), 2 deletions(-)
arch/arm64/Kconfig +11 -0
@@ -1610,6 +1610,17 @@ config RODATA_FULL_DEFAULT_ENABLED This requires the linear region to be mapped down to pages, which may adversely affect performance in some cases. +config ARM64_WXN + bool "Enable WXN attribute so all writable mappings are non-exec" + help + Set the WXN bit in the SCTLR system register so that all writable + mappings are treated as if the PXN/UXN bit is set as well. + If this is set to Y, it can still be disabled at runtime by + passing 'arm64.nowxn' on the kernel command line. + + This should only be set if no software needs to be supported that + relies on being able to execute from writable mappings. + config ARM64_SW_TTBR0_PAN bool "Emulate Privileged Access Never using TTBR0_EL1 switching" help
arch/arm64/include/asm/cpufeature.h +8 -0
@@ -18,6 +18,7 @@#define ARM64_SW_FEATURE_OVERRIDE_NOKASLR 0#define ARM64_SW_FEATURE_OVERRIDE_HVHE 4#define ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF 8 +#define ARM64_SW_FEATURE_OVERRIDE_NOWXN 12#ifndef __ASSEMBLY__
@@ -962,6 +963,13 @@ static inline bool kaslr_disabled_cmdline(void)return arm64_test_sw_feature_override (ARM64_SW_FEATURE_OVERRIDE_NOKASLR ); } +static inline bool arm64_wxn_enabled (void ) +{ +if (!IS_ENABLED (CONFIG_ARM64_WXN )) +return false; +return !arm64_test_sw_feature_override (ARM64_SW_FEATURE_OVERRIDE_NOWXN ); +} +u32 get_kvm_ipa_limit (void );void dump_cpu_features (void );
arch/arm64/include/asm/mman.h +36 -0
@@ -35,11 +35,40 @@ static inline unsigned long arch_calc_vm_flag_bits(unsigned long flags) }#define arch_calc_vm_flag_bits (flags) arch_calc_vm_flag_bits(flags) +static inline bool arm64_check_wx_prot (unsigned long prot, +struct task_struct *tsk) +{ +/* +* When we are running with SCTLR_ELx.WXN==1, writable mappings are +* implicitly non-executable. This means we should reject such mappings +* when user space attempts to create them using mmap() or mprotect(). +*/ +if (arm64_wxn_enabled () && + ((prot & (PROT_WRITE |PROT_EXEC )) == (PROT_WRITE |PROT_EXEC ))) { +/* +* User space libraries such as libffi carry elaborate +* heuristics to decide whether it is worth it to even attempt +* to create writable executable mappings, as PaX or selinux +* enabled systems will outright reject it. They will usually +* fall back to something else (e.g., two separate shared +* mmap()s of a temporary file) on failure. +*/ +pr_info_ratelimited ( +"process %s (%d) attempted to create PROT_WRITE+PROT_EXEC mapping\n" , + tsk->comm, tsk->pid); +return false; + } +return true; +} +static inline bool arch_validate_prot (unsigned long prot,unsigned long addr__always_unused ) {unsigned long supported =PROT_READ |PROT_WRITE |PROT_EXEC |PROT_SEM ; +if (!arm64_check_wx_prot (prot, current)) +return false; +if (system_supports_bti ()) supported |=PROT_BTI ;
@@ -50,6 +79,13 @@ static inline bool arch_validate_prot(unsigned long prot, }#define arch_validate_prot (prot, addr) arch_validate_prot(prot, addr) +static inline bool arch_validate_mmap_prot (unsigned long prot, +unsigned long addr) +{ +return arm64_check_wx_prot (prot, current); +} +#define arch_validate_mmap_prot arch_validate_mmap_prot +static inline bool arch_validate_flags (unsigned long vm_flags) {if (!system_supports_mte ())
arch/arm64/include/asm/mmu_context.h +29 -1
@@ -20,13 +20,41 @@#include <asm/cpufeature.h> #include <asm/daifflags.h> #include <asm/proc-fns.h> -#include <asm-generic/mm_hooks.h> #include <asm/cputype.h> #include <asm/sysreg.h> #include <asm/tlbflush.h> extern bool rodata_full; +static inline int arch_dup_mmap (struct mm_struct *oldmm, +struct mm_struct *mm) +{ +return 0 ; +} + +static inline void arch_exit_mmap (struct mm_struct *mm) +{ +} + +static inline void arch_unmap (struct mm_struct *mm, +unsigned long start,unsigned long end) +{ +} + +static inline bool arch_vma_access_permitted (struct vm_area_struct *vma, +bool write,bool execute,bool foreign) +{ +if (IS_ENABLED (CONFIG_ARM64_WXN ) && execute && + (vma->vm_flags & (VM_WRITE |VM_EXEC )) == (VM_WRITE |VM_EXEC )) { +pr_warn_ratelimited ( +"process %s (%d) attempted to execute from writable memory\n" , + current->comm, current->pid); +/* disallow unless the nowxn override is set */ +return !arm64_wxn_enabled (); + } +return true; +} +static inline void contextidr_thread_switch (struct task_struct *next) {if (!IS_ENABLED (CONFIG_PID_IN_CONTEXTIDR ))
arch/arm64/kernel/pi/idreg-override.c +3 -1
@@ -189,6 +189,7 @@ static const struct ftr_set_desc sw_features __prel64_initconst = { FIELD("nokaslr", ARM64_SW_FEATURE_OVERRIDE_NOKASLR, NULL), FIELD("hvhe", ARM64_SW_FEATURE_OVERRIDE_HVHE, hvhe_filter), FIELD("rodataoff", ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF, NULL), + FIELD("nowxn", ARM64_SW_FEATURE_OVERRIDE_NOWXN, NULL), {} }, };
@@ -221,8 +222,9 @@ static const struct { { "arm64.nomops", "id_aa64isar2.mops=0" }, { "arm64.nomte", "id_aa64pfr1.mte=0" }, { "nokaslr", "arm64_sw.nokaslr=1" }, - { "rodata=off", "arm64_sw.rodataoff=1" }, + { "rodata=off", "arm64_sw.rodataoff=1 arm64_sw.nowxn=1" }, { "arm64.nolva", "id_aa64mmfr2.varange=0" }, + { "arm64.nowxn", "arm64_sw.nowxn=1" }, };static int __init parse_hexdigit (const char *p,u64 *v)
arch/arm64/kernel/pi/map_kernel.c +23 -0
@@ -132,6 +132,25 @@ static void __init map_kernel(u64 kaslr_offset, u64 va_offset, int root_level)idmap_cpu_replace_ttbr1 (swapper_pg_dir); } +static void noinline __section(".idmap.text") disable_wxn(void) +{ +u64 sctlr =read_sysreg (sctlr_el1) & ~SCTLR_ELx_WXN; + +/* +* We cannot safely clear the WXN bit while the MMU and caches are on, +* so turn the MMU off, flush the TLBs and turn it on again but with +* the WXN bit cleared this time. +*/ + asm(" msr sctlr_el1, %0 ;" +" isb ;" +" tlbi vmalle1 ;" +" dsb nsh ;" +" isb ;" +" msr sctlr_el1, %1 ;" +" isb ;" + ::"r" (sctlr & ~SCTLR_ELx_M),"r" (sctlr)); +} + static void noinline __section(".idmap.text") set_ttbr0_for_lpa2(u64 ttbr) {u64 sctlr =read_sysreg (sctlr_el1);
@@ -229,6 +248,10 @@ asmlinkage void __init early_map_kernel(u64 boot_status, void *fdt)if (va_bits >VA_BITS_MIN )sysreg_clear_set (tcr_el1,TCR_T1SZ_MASK ,TCR_T1SZ (va_bits)); +if (IS_ENABLED (CONFIG_ARM64_WXN ) && +arm64_test_sw_feature_override (ARM64_SW_FEATURE_OVERRIDE_NOWXN )) +disable_wxn (); +/* * The virtual KASLR displacement modulo 2MiB is decided by the * physical placement of the image, as otherwise, we might not be able
arch/arm64/mm/proc.S +6 -0
@@ -546,6 +546,12 @@ alternative_else_nop_endif * Prepare SCTLR */ mov_q x0, INIT_SCTLR_EL1_MMU_ON +#ifdef CONFIG_ARM64_WXN + ldr_l x1, arm64_sw_feature_override + FTR_OVR_VAL_OFFSET + tst x1, #0xf << ARM64_SW_FEATURE_OVERRIDE_NOWXN + orr x1, x0, #SCTLR_ELx_WXN + csel x0, x0, x1, ne +#endif ret // return to head.S .unreq mair
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH v7 50/50 arm64: Set the default CONFIG_ARM64_VA_BITS_52 in Kconfig rather than defconfig
In reply to Ard BiesheuvelFrom: Catalin Marinas <[email protected]>
Currently the "Virtual address space size" arch/arm64/Kconfig menu entry sets different defaults for each page size. However, all are overridden by the defconfig to 52-bit. Set the actual default in Kconfig and remove the defconfig line.
--- arch/arm64/Kconfig | 4 +--- arch/arm64/configs/defconfig | 1 - 2 files changed, 1 insertion(+), 4 deletions(-)
arch/arm64/Kconfig +1 -3
@@ -1283,9 +1283,7 @@ endchoice choice prompt "Virtual address space size" - default ARM64_VA_BITS_39 if ARM64_4K_PAGES - default ARM64_VA_BITS_47 if ARM64_16K_PAGES - default ARM64_VA_BITS_42 if ARM64_64K_PAGES + default ARM64_VA_BITS_52 help Allows choosing one of multiple possible virtual address space sizes. The level of translation table is determined by
arch/arm64/configs/defconfig +0 -1
@@ -76,7 +76,6 @@ CONFIG_ARCH_VEXPRESS=y CONFIG_ARCH_VISCONTI=y CONFIG_ARCH_XGENE=y CONFIG_ARCH_ZYNQMP=y -CONFIG_ARM64_VA_BITS_52=y CONFIG_SCHED_MC=y CONFIG_SCHED_SMT=y CONFIG_NUMA=y
Signature · 7 lines
2.43.0.429.g432eaa2c6b-goog _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Quoted 9 lines · Ard Biesheuvel
From: Ard Biesheuvel <[email protected]>
This v7 covers the remaining changes that implement support for LPA2 and WXN at stage 1, now that some of the prerequisites are in place.
Ryan's KVM series for LPA2 at stage 2 has been merged in the mean time, so the temporary changes to plug that hole have been dropped.
... and I ended up dropping a patch that is still required:
https://lore.kernel.org/all/[email protected]/
which requires some minimal rebasing as well. I am not going to resend the whole series because of the single patch until it gains some more review traction.
Signature · 6 lines
Ard. _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel