arm64: mm: omit redundant remap of kernel image
authorArd Biesheuvel <ardb@kernel.org>
Wed, 14 Feb 2024 12:29:09 +0000 (13:29 +0100)
committerCatalin Marinas <catalin.marinas@arm.com>
Fri, 16 Feb 2024 12:42:35 +0000 (12:42 +0000)
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.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lore.kernel.org/r/20240214122845.2033971-68-ardb+git@google.com
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
arch/arm64/include/asm/fixmap.h
arch/arm64/include/asm/kasan.h
arch/arm64/include/asm/mmu.h
arch/arm64/kernel/image-vars.h
arch/arm64/kernel/pi/map_kernel.c
arch/arm64/mm/fixmap.c
arch/arm64/mm/kasan_init.c
arch/arm64/mm/mmu.c

index 58c294a..8aabd45 100644 (file)
@@ -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
 
index 7eefc52..e1b57c1 100644 (file)
 
 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
index d0b8b4b..65977c7 100644 (file)
@@ -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
index 941a14c..e140c5b 100644 (file)
@@ -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);
index f86e878..4b76a00 100644 (file)
@@ -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)
index 6fc17b2..9404f28 100644 (file)
@@ -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();
-       }
-}
index 4c7ad57..89828ad 100644 (file)
@@ -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)
 {
index 9758f7e..3db40b5 100644 (file)
@@ -648,9 +648,9 @@ void mark_rodata_ro(void)
        debug_checkwx();
 }
 
-static void __init map_kernel_segment(pgd_t *pgdp, void *va_start, void *va_end,
-                                     pgprot_t prot, struct vm_struct *vma,
-                                     int flags, unsigned long vm_flags)
+static void __init declare_vma(struct vm_struct *vma,
+                              void *va_start, void *va_end,
+                              unsigned long vm_flags)
 {
        phys_addr_t pa_start = __pa_symbol(va_start);
        unsigned long size = va_end - va_start;
@@ -658,9 +658,6 @@ static void __init map_kernel_segment(pgd_t *pgdp, void *va_start, void *va_end,
        BUG_ON(!PAGE_ALIGNED(pa_start));
        BUG_ON(!PAGE_ALIGNED(size));
 
-       __create_pgd_mapping(pgdp, pa_start, (unsigned long)va_start, size, prot,
-                            early_pgtable_alloc, flags);
-
        if (!(vm_flags & VM_NO_GUARD))
                size += PAGE_SIZE;
 
@@ -673,12 +670,12 @@ static void __init map_kernel_segment(pgd_t *pgdp, void *va_start, void *va_end,
        vm_area_add_early(vma);
 }
 
+#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
 static pgprot_t kernel_exec_prot(void)
 {
        return rodata_enabled ? PAGE_KERNEL_ROX : PAGE_KERNEL_EXEC;
 }
 
-#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
 static int __init map_entry_trampoline(void)
 {
        int i;
@@ -713,60 +710,17 @@ core_initcall(map_entry_trampoline);
 #endif
 
 /*
- * Open coded check for BTI, only for use to determine configuration
- * for early mappings for before the cpufeature code has run.
- */
-static bool arm64_early_this_cpu_has_bti(void)
-{
-       u64 pfr1;
-
-       if (!IS_ENABLED(CONFIG_ARM64_BTI_KERNEL))
-               return false;
-
-       pfr1 = __read_sysreg_by_encoding(SYS_ID_AA64PFR1_EL1);
-       return cpuid_feature_extract_unsigned_field(pfr1,
-                                                   ID_AA64PFR1_EL1_BT_SHIFT);
-}
-
-/*
- * Create fine-grained mappings for the kernel.
+ * Declare the VMA areas for the kernel
  */
-static void __init map_kernel(pgd_t *pgdp)
+static void __init declare_kernel_vmas(void)
 {
-       static struct vm_struct vmlinux_text, vmlinux_rodata, vmlinux_inittext,
-                               vmlinux_initdata, vmlinux_data;
-
-       /*
-        * External debuggers may need to write directly to the text
-        * mapping to install SW breakpoints. Allow this (only) when
-        * explicitly requested with rodata=off.
-        */
-       pgprot_t text_prot = kernel_exec_prot();
-
-       /*
-        * If we have a CPU that supports BTI and a kernel built for
-        * BTI then mark the kernel executable text as guarded pages
-        * now so we don't have to rewrite the page tables later.
-        */
-       if (arm64_early_this_cpu_has_bti())
-               text_prot = __pgprot_modify(text_prot, PTE_GP, PTE_GP);
+       static struct vm_struct vmlinux_seg[KERNEL_SEGMENT_COUNT];
 
-       /*
-        * Only rodata will be remapped with different permissions later on,
-        * all other segments are allowed to use contiguous mappings.
-        */
-       map_kernel_segment(pgdp, _stext, _etext, text_prot, &vmlinux_text, 0,
-                          VM_NO_GUARD);
-       map_kernel_segment(pgdp, __start_rodata, __inittext_begin, PAGE_KERNEL,
-                          &vmlinux_rodata, NO_CONT_MAPPINGS, VM_NO_GUARD);
-       map_kernel_segment(pgdp, __inittext_begin, __inittext_end, text_prot,
-                          &vmlinux_inittext, 0, VM_NO_GUARD);
-       map_kernel_segment(pgdp, __initdata_begin, __initdata_end, PAGE_KERNEL,
-                          &vmlinux_initdata, 0, VM_NO_GUARD);
-       map_kernel_segment(pgdp, _data, _end, PAGE_KERNEL, &vmlinux_data, 0, 0);
-
-       fixmap_copy(pgdp);
-       kasan_copy_shadow(pgdp);
+       declare_vma(&vmlinux_seg[0], _stext, _etext, VM_NO_GUARD);
+       declare_vma(&vmlinux_seg[1], __start_rodata, __inittext_begin, VM_NO_GUARD);
+       declare_vma(&vmlinux_seg[2], __inittext_begin, __inittext_end, VM_NO_GUARD);
+       declare_vma(&vmlinux_seg[3], __initdata_begin, __initdata_end, VM_NO_GUARD);
+       declare_vma(&vmlinux_seg[4], _data, _end, 0);
 }
 
 void __pi_map_range(u64 *pgd, u64 start, u64 end, u64 pa, pgprot_t prot,
@@ -802,23 +756,12 @@ static void __init create_idmap(void)
 
 void __init paging_init(void)
 {
-       pgd_t *pgdp = pgd_set_fixmap(__pa_symbol(swapper_pg_dir));
-       extern pgd_t init_idmap_pg_dir[];
-
-       map_kernel(pgdp);
-       map_mem(pgdp);
-
-       pgd_clear_fixmap();
-
-       cpu_replace_ttbr1(lm_alias(swapper_pg_dir), init_idmap_pg_dir);
-       init_mm.pgd = swapper_pg_dir;
-
-       memblock_phys_free(__pa_symbol(init_pg_dir),
-                          __pa_symbol(init_pg_end) - __pa_symbol(init_pg_dir));
+       map_mem(swapper_pg_dir);
 
        memblock_allow_resize();
 
        create_idmap();
+       declare_kernel_vmas();
 }
 
 #ifdef CONFIG_MEMORY_HOTPLUG