xref: /openbmc/linux/arch/arm/mm/pageattr.c (revision f9bff0e3)
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