parisc: Enable the ro_after_init feature
authorHelge Deller <deller@gmx.de>
Fri, 10 May 2019 15:00:01 +0000 (17:00 +0200)
committerHelge Deller <deller@gmx.de>
Fri, 10 May 2019 19:00:44 +0000 (21:00 +0200)
This patch modifies the initial page mapping functions in the following way:

During bootup the init, text and data pages will be mapped RWX and if
supported, with huge pages.

At final stage of the bootup, the kernel calls free_initmem() and then all
pages will be remapped either R-X (for text and read-only data) or RW- (for
data). The __init pages will be dropped.

This reflects the behaviour of the x86 platform.

Signed-off-by: Helge Deller <deller@gmx.de>
arch/parisc/include/asm/cache.h
arch/parisc/kernel/vmlinux.lds.S
arch/parisc/mm/init.c

index 4016fe1..73ca89a 100644 (file)
@@ -24,9 +24,6 @@
 
 #define __read_mostly __attribute__((__section__(".data..read_mostly")))
 
-/* Read-only memory is marked before mark_rodata_ro() is called. */
-#define __ro_after_init        __read_mostly
-
 void parisc_cache_init(void);  /* initializes cache-flushing */
 void disable_sr_hashing_asm(int); /* low level support for above */
 void disable_sr_hashing(void);   /* turns off space register hashing */
index a8be7a4..c3b1b9c 100644 (file)
@@ -18,9 +18,6 @@
                                *(.data..vm0.pgd) \
                                *(.data..vm0.pte)
 
-/* No __ro_after_init data in the .rodata section - which will always be ro */
-#define RO_AFTER_INIT_DATA
-
 #include <asm-generic/vmlinux.lds.h>
 
 /* needed for the processor specific cache alignment size */   
index 3b0f9ea..b99bcbf 100644 (file)
@@ -345,16 +345,7 @@ static void __init setup_bootmem(void)
        memblock_dump_all();
 }
 
-static int __init parisc_text_address(unsigned long vaddr)
-{
-       static unsigned long head_ptr __initdata;
-
-       if (!head_ptr)
-               head_ptr = PAGE_MASK & (unsigned long)
-                       dereference_function_descriptor(&parisc_kernel_start);
-
-       return core_kernel_text(vaddr) || vaddr == head_ptr;
-}
+static bool kernel_set_to_readonly;
 
 static void __init map_pages(unsigned long start_vaddr,
                             unsigned long start_paddr, unsigned long size,
@@ -372,10 +363,11 @@ static void __init map_pages(unsigned long start_vaddr,
        unsigned long vaddr;
        unsigned long ro_start;
        unsigned long ro_end;
-       unsigned long kernel_end;
+       unsigned long kernel_start, kernel_end;
 
        ro_start = __pa((unsigned long)_text);
        ro_end   = __pa((unsigned long)&data_start);
+       kernel_start = __pa((unsigned long)&__init_begin);
        kernel_end  = __pa((unsigned long)&_end);
 
        end_paddr = start_paddr + size;
@@ -438,26 +430,30 @@ static void __init map_pages(unsigned long start_vaddr,
                        pg_table = (pte_t *) __va(pg_table) + start_pte;
                        for (tmp2 = start_pte; tmp2 < PTRS_PER_PTE; tmp2++, pg_table++) {
                                pte_t pte;
-
-                               if (force)
-                                       pte =  __mk_pte(address, pgprot);
-                               else if (parisc_text_address(vaddr)) {
-                                       pte = __mk_pte(address, PAGE_KERNEL_EXEC);
-                                       if (address >= ro_start && address < kernel_end)
-                                               pte = pte_mkhuge(pte);
+                               pgprot_t prot;
+                               bool huge = false;
+
+                               if (force) {
+                                       prot = pgprot;
+                               } else if (address < kernel_start || address >= kernel_end) {
+                                       /* outside kernel memory */
+                                       prot = PAGE_KERNEL;
+                               } else if (!kernel_set_to_readonly) {
+                                       /* still initializing, allow writing to RO memory */
+                                       prot = PAGE_KERNEL_RWX;
+                                       huge = true;
+                               } else if (address >= ro_start) {
+                                       /* Code (ro) and Data areas */
+                                       prot = (address < ro_end) ?
+                                               PAGE_KERNEL_EXEC : PAGE_KERNEL;
+                                       huge = true;
+                               } else {
+                                       prot = PAGE_KERNEL;
                                }
-                               else
-#if defined(CONFIG_PARISC_PAGE_SIZE_4KB)
-                               if (address >= ro_start && address < ro_end) {
-                                       pte = __mk_pte(address, PAGE_KERNEL_EXEC);
+
+                               pte = __mk_pte(address, prot);
+                               if (huge)
                                        pte = pte_mkhuge(pte);
-                               } else
-#endif
-                               {
-                                       pte = __mk_pte(address, pgprot);
-                                       if (address >= ro_start && address < kernel_end)
-                                               pte = pte_mkhuge(pte);
-                               }
 
                                if (address >= end_paddr)
                                        break;
@@ -493,6 +489,12 @@ void __ref free_initmem(void)
 {
        unsigned long init_begin = (unsigned long)__init_begin;
        unsigned long init_end = (unsigned long)__init_end;
+       unsigned long kernel_end  = (unsigned long)&_end;
+
+       /* Remap kernel text and data, but do not touch init section yet. */
+       kernel_set_to_readonly = true;
+       map_pages(init_end, __pa(init_end), kernel_end - init_end,
+                 PAGE_KERNEL, 0);
 
        /* The init text pages are marked R-X.  We have to
         * flush the icache and mark them RW-
@@ -509,7 +511,7 @@ void __ref free_initmem(void)
                  PAGE_KERNEL, 1);
 
        /* force the kernel to see the new TLB entries */
-       __flush_tlb_range(0, init_begin, init_end);
+       __flush_tlb_range(0, init_begin, kernel_end);
 
        /* finally dump all the instructions which were cached, since the
         * pages are no-longer executable */
@@ -527,8 +529,9 @@ void mark_rodata_ro(void)
 {
        /* rodata memory was already mapped with KERNEL_RO access rights by
            pagetable_init() and map_pages(). No need to do additional stuff here */
-       printk (KERN_INFO "Write protecting the kernel read-only data: %luk\n",
-               (unsigned long)(__end_rodata - __start_rodata) >> 10);
+       unsigned long roai_size = __end_ro_after_init - __start_ro_after_init;
+
+       pr_info("Write protected read-only-after-init data: %luk\n", roai_size >> 10);
 }
 #endif