197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24e802cfdSJungseung Lee /*
34e802cfdSJungseung Lee * Copyright (c) 2014, The Linux Foundation. All rights reserved.
44e802cfdSJungseung Lee */
54e802cfdSJungseung Lee #include <linux/mm.h>
64e802cfdSJungseung Lee #include <linux/module.h>
74e802cfdSJungseung Lee
84e802cfdSJungseung Lee #include <asm/tlbflush.h>
974d86a70SLaura Abbott #include <asm/set_memory.h>
104e802cfdSJungseung Lee
114e802cfdSJungseung Lee struct page_change_data {
124e802cfdSJungseung Lee pgprot_t set_mask;
134e802cfdSJungseung Lee pgprot_t clear_mask;
144e802cfdSJungseung Lee };
154e802cfdSJungseung Lee
change_page_range(pte_t * ptep,unsigned long addr,void * data)168b1e0f81SAnshuman Khandual static int change_page_range(pte_t *ptep, unsigned long addr, void *data)
174e802cfdSJungseung Lee {
184e802cfdSJungseung Lee struct page_change_data *cdata = data;
194e802cfdSJungseung Lee pte_t pte = *ptep;
204e802cfdSJungseung Lee
214e802cfdSJungseung Lee pte = clear_pte_bit(pte, cdata->clear_mask);
224e802cfdSJungseung Lee pte = set_pte_bit(pte, cdata->set_mask);
234e802cfdSJungseung Lee
244e802cfdSJungseung Lee set_pte_ext(ptep, pte, 0);
254e802cfdSJungseung Lee return 0;
264e802cfdSJungseung Lee }
274e802cfdSJungseung Lee
range_in_range(unsigned long start,unsigned long size,unsigned long range_start,unsigned long range_end)28*f9bff0e3SMatthew Wilcox (Oracle) static bool range_in_range(unsigned long start, unsigned long size,
29580218f9SRussell King unsigned long range_start, unsigned long range_end)
30580218f9SRussell King {
31580218f9SRussell King return start >= range_start && start < range_end &&
32580218f9SRussell King size <= range_end - start;
33580218f9SRussell King }
34580218f9SRussell King
350ba8695eSWang Kefeng /*
360ba8695eSWang Kefeng * This function assumes that the range is mapped with PAGE_SIZE pages.
370ba8695eSWang Kefeng */
__change_memory_common(unsigned long start,unsigned long size,pgprot_t set_mask,pgprot_t clear_mask)380ba8695eSWang Kefeng static int __change_memory_common(unsigned long start, unsigned long size,
390ba8695eSWang Kefeng pgprot_t set_mask, pgprot_t clear_mask)
400ba8695eSWang Kefeng {
410ba8695eSWang Kefeng struct page_change_data data;
420ba8695eSWang Kefeng int ret;
430ba8695eSWang Kefeng
440ba8695eSWang Kefeng data.set_mask = set_mask;
450ba8695eSWang Kefeng data.clear_mask = clear_mask;
460ba8695eSWang Kefeng
470ba8695eSWang Kefeng ret = apply_to_page_range(&init_mm, start, size, change_page_range,
480ba8695eSWang Kefeng &data);
490ba8695eSWang Kefeng
500ba8695eSWang Kefeng flush_tlb_kernel_range(start, start + size);
510ba8695eSWang Kefeng return ret;
520ba8695eSWang Kefeng }
530ba8695eSWang Kefeng
change_memory_common(unsigned long addr,int numpages,pgprot_t set_mask,pgprot_t clear_mask)544e802cfdSJungseung Lee static int change_memory_common(unsigned long addr, int numpages,
554e802cfdSJungseung Lee pgprot_t set_mask, pgprot_t clear_mask)
564e802cfdSJungseung Lee {
57580218f9SRussell King unsigned long start = addr & PAGE_MASK;
58580218f9SRussell King unsigned long end = PAGE_ALIGN(addr) + numpages * PAGE_SIZE;
59580218f9SRussell King unsigned long size = end - start;
604e802cfdSJungseung Lee
61580218f9SRussell King WARN_ON_ONCE(start != addr);
624e802cfdSJungseung Lee
63580218f9SRussell King if (!size)
64f474c8c8SMika Penttilä return 0;
65f474c8c8SMika Penttilä
66*f9bff0e3SMatthew Wilcox (Oracle) if (!range_in_range(start, size, MODULES_VADDR, MODULES_END) &&
67*f9bff0e3SMatthew Wilcox (Oracle) !range_in_range(start, size, VMALLOC_START, VMALLOC_END))
684e802cfdSJungseung Lee return -EINVAL;
694e802cfdSJungseung Lee
700ba8695eSWang Kefeng return __change_memory_common(start, size, set_mask, clear_mask);
714e802cfdSJungseung Lee }
724e802cfdSJungseung Lee
set_memory_ro(unsigned long addr,int numpages)734e802cfdSJungseung Lee int set_memory_ro(unsigned long addr, int numpages)
744e802cfdSJungseung Lee {
754e802cfdSJungseung Lee return change_memory_common(addr, numpages,
764e802cfdSJungseung Lee __pgprot(L_PTE_RDONLY),
774e802cfdSJungseung Lee __pgprot(0));
784e802cfdSJungseung Lee }
794e802cfdSJungseung Lee
set_memory_rw(unsigned long addr,int numpages)804e802cfdSJungseung Lee int set_memory_rw(unsigned long addr, int numpages)
814e802cfdSJungseung Lee {
824e802cfdSJungseung Lee return change_memory_common(addr, numpages,
834e802cfdSJungseung Lee __pgprot(0),
844e802cfdSJungseung Lee __pgprot(L_PTE_RDONLY));
854e802cfdSJungseung Lee }
864e802cfdSJungseung Lee
set_memory_nx(unsigned long addr,int numpages)874e802cfdSJungseung Lee int set_memory_nx(unsigned long addr, int numpages)
884e802cfdSJungseung Lee {
894e802cfdSJungseung Lee return change_memory_common(addr, numpages,
904e802cfdSJungseung Lee __pgprot(L_PTE_XN),
914e802cfdSJungseung Lee __pgprot(0));
924e802cfdSJungseung Lee }
934e802cfdSJungseung Lee
set_memory_x(unsigned long addr,int numpages)944e802cfdSJungseung Lee int set_memory_x(unsigned long addr, int numpages)
954e802cfdSJungseung Lee {
964e802cfdSJungseung Lee return change_memory_common(addr, numpages,
974e802cfdSJungseung Lee __pgprot(0),
984e802cfdSJungseung Lee __pgprot(L_PTE_XN));
994e802cfdSJungseung Lee }
1000ba8695eSWang Kefeng
set_memory_valid(unsigned long addr,int numpages,int enable)1010ba8695eSWang Kefeng int set_memory_valid(unsigned long addr, int numpages, int enable)
1020ba8695eSWang Kefeng {
1030ba8695eSWang Kefeng if (enable)
1040ba8695eSWang Kefeng return __change_memory_common(addr, PAGE_SIZE * numpages,
1050ba8695eSWang Kefeng __pgprot(L_PTE_VALID),
1060ba8695eSWang Kefeng __pgprot(0));
1070ba8695eSWang Kefeng else
1080ba8695eSWang Kefeng return __change_memory_common(addr, PAGE_SIZE * numpages,
1090ba8695eSWang Kefeng __pgprot(0),
1100ba8695eSWang Kefeng __pgprot(L_PTE_VALID));
1110ba8695eSWang Kefeng }
112