162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci/* 462306a36Sopenharmony_ci * MMU-generic set_memory implementation for powerpc 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright 2019-2021, IBM Corporation. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/vmalloc.h> 1162306a36Sopenharmony_ci#include <linux/set_memory.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <asm/mmu.h> 1462306a36Sopenharmony_ci#include <asm/page.h> 1562306a36Sopenharmony_ci#include <asm/pgtable.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic pte_basic_t pte_update_delta(pte_t *ptep, unsigned long addr, 1962306a36Sopenharmony_ci unsigned long old, unsigned long new) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci return pte_update(&init_mm, addr, ptep, old & ~new, new & ~old, 0); 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Updates the attributes of a page atomically. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * This sequence is safe against concurrent updates, and also allows updating the 2862306a36Sopenharmony_ci * attributes of a page currently being executed or accessed. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic int change_page_attr(pte_t *ptep, unsigned long addr, void *data) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci long action = (long)data; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci addr &= PAGE_MASK; 3562306a36Sopenharmony_ci /* modify the PTE bits as desired */ 3662306a36Sopenharmony_ci switch (action) { 3762306a36Sopenharmony_ci case SET_MEMORY_RO: 3862306a36Sopenharmony_ci /* Don't clear DIRTY bit */ 3962306a36Sopenharmony_ci pte_update_delta(ptep, addr, _PAGE_KERNEL_RW & ~_PAGE_DIRTY, _PAGE_KERNEL_RO); 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case SET_MEMORY_RW: 4262306a36Sopenharmony_ci pte_update_delta(ptep, addr, _PAGE_KERNEL_RO, _PAGE_KERNEL_RW); 4362306a36Sopenharmony_ci break; 4462306a36Sopenharmony_ci case SET_MEMORY_NX: 4562306a36Sopenharmony_ci pte_update_delta(ptep, addr, _PAGE_KERNEL_ROX, _PAGE_KERNEL_RO); 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci case SET_MEMORY_X: 4862306a36Sopenharmony_ci pte_update_delta(ptep, addr, _PAGE_KERNEL_RO, _PAGE_KERNEL_ROX); 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci case SET_MEMORY_NP: 5162306a36Sopenharmony_ci pte_update(&init_mm, addr, ptep, _PAGE_PRESENT, 0, 0); 5262306a36Sopenharmony_ci break; 5362306a36Sopenharmony_ci case SET_MEMORY_P: 5462306a36Sopenharmony_ci pte_update(&init_mm, addr, ptep, 0, _PAGE_PRESENT, 0); 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci default: 5762306a36Sopenharmony_ci WARN_ON_ONCE(1); 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* See ptesync comment in radix__set_pte_at() */ 6262306a36Sopenharmony_ci if (radix_enabled()) 6362306a36Sopenharmony_ci asm volatile("ptesync": : :"memory"); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci flush_tlb_kernel_range(addr, addr + PAGE_SIZE); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ciint change_memory_attr(unsigned long addr, int numpages, long action) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci unsigned long start = ALIGN_DOWN(addr, PAGE_SIZE); 7362306a36Sopenharmony_ci unsigned long size = numpages * PAGE_SIZE; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!numpages) 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (WARN_ON_ONCE(is_vmalloc_or_module_addr((void *)addr) && 7962306a36Sopenharmony_ci is_vm_area_hugepages((void *)addr))) 8062306a36Sopenharmony_ci return -EINVAL; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64 8362306a36Sopenharmony_ci /* 8462306a36Sopenharmony_ci * On hash, the linear mapping is not in the Linux page table so 8562306a36Sopenharmony_ci * apply_to_existing_page_range() will have no effect. If in the future 8662306a36Sopenharmony_ci * the set_memory_* functions are used on the linear map this will need 8762306a36Sopenharmony_ci * to be updated. 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_ci if (!radix_enabled()) { 9062306a36Sopenharmony_ci int region = get_region_id(addr); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (WARN_ON_ONCE(region != VMALLOC_REGION_ID && region != IO_REGION_ID)) 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci#endif 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci return apply_to_existing_page_range(&init_mm, start, size, 9862306a36Sopenharmony_ci change_page_attr, (void *)action); 9962306a36Sopenharmony_ci} 100