Add set_memory_ro/rw/x/nx/rw_nx architecture hooks. These set_memory_* and set_pages_* APIs can make it easier to change the attributes of memory or pages. Similar to the implementation of riscv. The declaration of set_memory_* functions uses the general set_memory.h (i.e. include/asm-generic/set_memory.h). Signed-off-by: Youling Tang <tangyouling@xxxxxxxxxxx> --- arch/mips/Kconfig | 1 + arch/mips/include/asm/set_memory.h | 19 +++++ arch/mips/mm/Makefile | 1 + arch/mips/mm/pageattr.c | 157 +++++++++++++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 arch/mips/include/asm/set_memory.h create mode 100644 arch/mips/mm/pageattr.c diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index e9893cd..ddeefd4 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -12,6 +12,7 @@ config MIPS select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAS_UBSAN_SANITIZE_ALL select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_HAS_SET_MEMORY select ARCH_KEEP_MEMBLOCK if DEBUG_KERNEL select ARCH_SUPPORTS_UPROBES select ARCH_USE_BUILTIN_BSWAP diff --git a/arch/mips/include/asm/set_memory.h b/arch/mips/include/asm/set_memory.h new file mode 100644 index 0000000..42217eb --- /dev/null +++ b/arch/mips/include/asm/set_memory.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _ASM_MIPS_SET_MEMORY_H +#define _ASM_MIPS_SET_MEMORY_H + +#include <asm-generic/set_memory.h> + +struct pageattr_masks { + pgprot_t set_mask; + pgprot_t clear_mask; +}; + +/* + * Functions to change pages attributes. + */ +int set_pages_ro(struct page *page, int numpages); +int set_pages_rw(struct page *page, int numpages); + +#endif /* _ASM_MIPS_SET_MEMORY_H */ diff --git a/arch/mips/mm/Makefile b/arch/mips/mm/Makefile index 4acc4f3..2022940 100644 --- a/arch/mips/mm/Makefile +++ b/arch/mips/mm/Makefile @@ -12,6 +12,7 @@ obj-y += mmap.o obj-y += page.o obj-y += page-funcs.o obj-y += pgtable.o +obj-y += pageattr.o obj-y += tlbex.o obj-y += tlbex-fault.o obj-y += tlb-funcs.o diff --git a/arch/mips/mm/pageattr.c b/arch/mips/mm/pageattr.c new file mode 100644 index 0000000..59db16e --- /dev/null +++ b/arch/mips/mm/pageattr.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/pagewalk.h> +#include <linux/pgtable.h> +#include <asm/tlbflush.h> +#include <asm/bitops.h> +#include <asm/set_memory.h> + +static unsigned long set_pageattr_masks(unsigned long val, struct mm_walk *walk) +{ + struct pageattr_masks *masks = walk->private; + unsigned long new_val = val; + + new_val &= ~(pgprot_val(masks->clear_mask)); + new_val |= (pgprot_val(masks->set_mask)); + + return new_val; +} + +static int pageattr_pgd_entry(pgd_t *pgd, unsigned long addr, + unsigned long next, struct mm_walk *walk) +{ + pgd_t val = READ_ONCE(*pgd); + + if (pgd_leaf(val)) { + val = __pgd(set_pageattr_masks(pgd_val(val), walk)); + set_pgd(pgd, val); + } + + return 0; +} + +static int pageattr_p4d_entry(p4d_t *p4d, unsigned long addr, + unsigned long next, struct mm_walk *walk) +{ + p4d_t val = READ_ONCE(*p4d); + + if (p4d_leaf(val)) { + val = __p4d(set_pageattr_masks(p4d_val(val), walk)); + set_p4d(p4d, val); + } + + return 0; +} + +static int pageattr_pud_entry(pud_t *pud, unsigned long addr, + unsigned long next, struct mm_walk *walk) +{ + pud_t val = READ_ONCE(*pud); + + if (pud_leaf(val)) { + val = __pud(set_pageattr_masks(pud_val(val), walk)); + set_pud(pud, val); + } + + return 0; +} + +static int pageattr_pmd_entry(pmd_t *pmd, unsigned long addr, + unsigned long next, struct mm_walk *walk) +{ + pmd_t val = READ_ONCE(*pmd); + + if (pmd_leaf(val)) { + val = __pmd(set_pageattr_masks(pmd_val(val), walk)); + set_pmd(pmd, val); + } + + return 0; +} + +static int pageattr_pte_entry(pte_t *pte, unsigned long addr, + unsigned long next, struct mm_walk *walk) +{ + pte_t val = READ_ONCE(*pte); + + val = __pte(set_pageattr_masks(pte_val(val), walk)); + set_pte(pte, val); + + return 0; +} + +static int pageattr_pte_hole(unsigned long addr, unsigned long next, + int depth, struct mm_walk *walk) +{ + /* Nothing to do here */ + return 0; +} + +static const struct mm_walk_ops pageattr_ops = { + .pgd_entry = pageattr_pgd_entry, + .p4d_entry = pageattr_p4d_entry, + .pud_entry = pageattr_pud_entry, + .pmd_entry = pageattr_pmd_entry, + .pte_entry = pageattr_pte_entry, + .pte_hole = pageattr_pte_hole, +}; + +static int __set_memory(unsigned long addr, int numpages, pgprot_t set_mask, + pgprot_t clear_mask) +{ + int ret; + unsigned long start = addr; + unsigned long end = start + PAGE_SIZE * numpages; + struct pageattr_masks masks = { + .set_mask = set_mask, + .clear_mask = clear_mask + }; + + if (!numpages) + return 0; + + mmap_read_lock(&init_mm); + ret = walk_page_range_novma(&init_mm, start, end, &pageattr_ops, NULL, + &masks); + mmap_read_unlock(&init_mm); + + flush_tlb_kernel_range(start, end); + + return ret; +} + +int set_memory_ro(unsigned long addr, int numpages) +{ + return __set_memory(addr, numpages, __pgprot(__READABLE), + __pgprot(_PAGE_WRITE)); +} + +int set_memory_rw(unsigned long addr, int numpages) +{ + return __set_memory(addr, numpages, __pgprot(__WRITEABLE | __READABLE), + __pgprot(0)); +} + +int set_memory_x(unsigned long addr, int numpages) +{ + return __set_memory(addr, numpages, __pgprot(0), __pgprot(_PAGE_NO_EXEC)); +} + +int set_memory_nx(unsigned long addr, int numpages) +{ + return __set_memory(addr, numpages, __pgprot(_PAGE_NO_EXEC), __pgprot(0)); +} + +int set_pages_ro(struct page *page, int numpages) +{ + unsigned long addr = (unsigned long)page_address(page); + + return set_memory_ro(addr, numpages); +} + +int set_pages_rw(struct page *page, int numpages) +{ + unsigned long addr = (unsigned long)page_address(page); + + return set_memory_rw(addr, numpages); +} -- 2.1.0