parisc: Enable the ro_after_init feature

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>
This commit is contained in:
Helge Deller 2019-05-10 17:00:01 +02:00
parent e6eb5fe912
commit 8d0e051cc7
3 changed files with 34 additions and 37 deletions

View 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 */

View 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 */

View 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;
pgprot_t prot;
bool huge = false;
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);
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