162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright IBM Corp. 2011 462306a36Sopenharmony_ci * Author(s): Jan Glauber <jang@linux.vnet.ibm.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/hugetlb.h> 762306a36Sopenharmony_ci#include <linux/proc_fs.h> 862306a36Sopenharmony_ci#include <linux/vmalloc.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <asm/cacheflush.h> 1162306a36Sopenharmony_ci#include <asm/facility.h> 1262306a36Sopenharmony_ci#include <asm/pgalloc.h> 1362306a36Sopenharmony_ci#include <asm/kfence.h> 1462306a36Sopenharmony_ci#include <asm/page.h> 1562306a36Sopenharmony_ci#include <asm/set_memory.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic inline unsigned long sske_frame(unsigned long addr, unsigned char skey) 1862306a36Sopenharmony_ci{ 1962306a36Sopenharmony_ci asm volatile(".insn rrf,0xb22b0000,%[skey],%[addr],1,0" 2062306a36Sopenharmony_ci : [addr] "+a" (addr) : [skey] "d" (skey)); 2162306a36Sopenharmony_ci return addr; 2262306a36Sopenharmony_ci} 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_civoid __storage_key_init_range(unsigned long start, unsigned long end) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci unsigned long boundary, size; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci while (start < end) { 2962306a36Sopenharmony_ci if (MACHINE_HAS_EDAT1) { 3062306a36Sopenharmony_ci /* set storage keys for a 1MB frame */ 3162306a36Sopenharmony_ci size = 1UL << 20; 3262306a36Sopenharmony_ci boundary = (start + size) & ~(size - 1); 3362306a36Sopenharmony_ci if (boundary <= end) { 3462306a36Sopenharmony_ci do { 3562306a36Sopenharmony_ci start = sske_frame(start, PAGE_DEFAULT_KEY); 3662306a36Sopenharmony_ci } while (start < boundary); 3762306a36Sopenharmony_ci continue; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci page_set_storage_key(start, PAGE_DEFAULT_KEY, 1); 4162306a36Sopenharmony_ci start += PAGE_SIZE; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 4662306a36Sopenharmony_ciatomic_long_t __bootdata_preserved(direct_pages_count[PG_DIRECT_MAP_MAX]); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_civoid arch_report_meminfo(struct seq_file *m) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci seq_printf(m, "DirectMap4k: %8lu kB\n", 5162306a36Sopenharmony_ci atomic_long_read(&direct_pages_count[PG_DIRECT_MAP_4K]) << 2); 5262306a36Sopenharmony_ci seq_printf(m, "DirectMap1M: %8lu kB\n", 5362306a36Sopenharmony_ci atomic_long_read(&direct_pages_count[PG_DIRECT_MAP_1M]) << 10); 5462306a36Sopenharmony_ci seq_printf(m, "DirectMap2G: %8lu kB\n", 5562306a36Sopenharmony_ci atomic_long_read(&direct_pages_count[PG_DIRECT_MAP_2G]) << 21); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci#endif /* CONFIG_PROC_FS */ 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void pgt_set(unsigned long *old, unsigned long new, unsigned long addr, 6062306a36Sopenharmony_ci unsigned long dtt) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci unsigned long *table, mask; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci mask = 0; 6562306a36Sopenharmony_ci if (MACHINE_HAS_EDAT2) { 6662306a36Sopenharmony_ci switch (dtt) { 6762306a36Sopenharmony_ci case CRDTE_DTT_REGION3: 6862306a36Sopenharmony_ci mask = ~(PTRS_PER_PUD * sizeof(pud_t) - 1); 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci case CRDTE_DTT_SEGMENT: 7162306a36Sopenharmony_ci mask = ~(PTRS_PER_PMD * sizeof(pmd_t) - 1); 7262306a36Sopenharmony_ci break; 7362306a36Sopenharmony_ci case CRDTE_DTT_PAGE: 7462306a36Sopenharmony_ci mask = ~(PTRS_PER_PTE * sizeof(pte_t) - 1); 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci table = (unsigned long *)((unsigned long)old & mask); 7862306a36Sopenharmony_ci crdte(*old, new, table, dtt, addr, S390_lowcore.kernel_asce); 7962306a36Sopenharmony_ci } else if (MACHINE_HAS_IDTE) { 8062306a36Sopenharmony_ci cspg(old, *old, new); 8162306a36Sopenharmony_ci } else { 8262306a36Sopenharmony_ci csp((unsigned int *)old + 1, *old, new); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int walk_pte_level(pmd_t *pmdp, unsigned long addr, unsigned long end, 8762306a36Sopenharmony_ci unsigned long flags) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci pte_t *ptep, new; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (flags == SET_MEMORY_4K) 9262306a36Sopenharmony_ci return 0; 9362306a36Sopenharmony_ci ptep = pte_offset_kernel(pmdp, addr); 9462306a36Sopenharmony_ci do { 9562306a36Sopenharmony_ci new = *ptep; 9662306a36Sopenharmony_ci if (pte_none(new)) 9762306a36Sopenharmony_ci return -EINVAL; 9862306a36Sopenharmony_ci if (flags & SET_MEMORY_RO) 9962306a36Sopenharmony_ci new = pte_wrprotect(new); 10062306a36Sopenharmony_ci else if (flags & SET_MEMORY_RW) 10162306a36Sopenharmony_ci new = pte_mkwrite_novma(pte_mkdirty(new)); 10262306a36Sopenharmony_ci if (flags & SET_MEMORY_NX) 10362306a36Sopenharmony_ci new = set_pte_bit(new, __pgprot(_PAGE_NOEXEC)); 10462306a36Sopenharmony_ci else if (flags & SET_MEMORY_X) 10562306a36Sopenharmony_ci new = clear_pte_bit(new, __pgprot(_PAGE_NOEXEC)); 10662306a36Sopenharmony_ci if (flags & SET_MEMORY_INV) { 10762306a36Sopenharmony_ci new = set_pte_bit(new, __pgprot(_PAGE_INVALID)); 10862306a36Sopenharmony_ci } else if (flags & SET_MEMORY_DEF) { 10962306a36Sopenharmony_ci new = __pte(pte_val(new) & PAGE_MASK); 11062306a36Sopenharmony_ci new = set_pte_bit(new, PAGE_KERNEL); 11162306a36Sopenharmony_ci if (!MACHINE_HAS_NX) 11262306a36Sopenharmony_ci new = clear_pte_bit(new, __pgprot(_PAGE_NOEXEC)); 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci pgt_set((unsigned long *)ptep, pte_val(new), addr, CRDTE_DTT_PAGE); 11562306a36Sopenharmony_ci ptep++; 11662306a36Sopenharmony_ci addr += PAGE_SIZE; 11762306a36Sopenharmony_ci cond_resched(); 11862306a36Sopenharmony_ci } while (addr < end); 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int split_pmd_page(pmd_t *pmdp, unsigned long addr) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci unsigned long pte_addr, prot; 12562306a36Sopenharmony_ci pte_t *pt_dir, *ptep; 12662306a36Sopenharmony_ci pmd_t new; 12762306a36Sopenharmony_ci int i, ro, nx; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci pt_dir = vmem_pte_alloc(); 13062306a36Sopenharmony_ci if (!pt_dir) 13162306a36Sopenharmony_ci return -ENOMEM; 13262306a36Sopenharmony_ci pte_addr = pmd_pfn(*pmdp) << PAGE_SHIFT; 13362306a36Sopenharmony_ci ro = !!(pmd_val(*pmdp) & _SEGMENT_ENTRY_PROTECT); 13462306a36Sopenharmony_ci nx = !!(pmd_val(*pmdp) & _SEGMENT_ENTRY_NOEXEC); 13562306a36Sopenharmony_ci prot = pgprot_val(ro ? PAGE_KERNEL_RO : PAGE_KERNEL); 13662306a36Sopenharmony_ci if (!nx) 13762306a36Sopenharmony_ci prot &= ~_PAGE_NOEXEC; 13862306a36Sopenharmony_ci ptep = pt_dir; 13962306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PTE; i++) { 14062306a36Sopenharmony_ci set_pte(ptep, __pte(pte_addr | prot)); 14162306a36Sopenharmony_ci pte_addr += PAGE_SIZE; 14262306a36Sopenharmony_ci ptep++; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci new = __pmd(__pa(pt_dir) | _SEGMENT_ENTRY); 14562306a36Sopenharmony_ci pgt_set((unsigned long *)pmdp, pmd_val(new), addr, CRDTE_DTT_SEGMENT); 14662306a36Sopenharmony_ci update_page_count(PG_DIRECT_MAP_4K, PTRS_PER_PTE); 14762306a36Sopenharmony_ci update_page_count(PG_DIRECT_MAP_1M, -1); 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void modify_pmd_page(pmd_t *pmdp, unsigned long addr, 15262306a36Sopenharmony_ci unsigned long flags) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci pmd_t new = *pmdp; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (flags & SET_MEMORY_RO) 15762306a36Sopenharmony_ci new = pmd_wrprotect(new); 15862306a36Sopenharmony_ci else if (flags & SET_MEMORY_RW) 15962306a36Sopenharmony_ci new = pmd_mkwrite_novma(pmd_mkdirty(new)); 16062306a36Sopenharmony_ci if (flags & SET_MEMORY_NX) 16162306a36Sopenharmony_ci new = set_pmd_bit(new, __pgprot(_SEGMENT_ENTRY_NOEXEC)); 16262306a36Sopenharmony_ci else if (flags & SET_MEMORY_X) 16362306a36Sopenharmony_ci new = clear_pmd_bit(new, __pgprot(_SEGMENT_ENTRY_NOEXEC)); 16462306a36Sopenharmony_ci if (flags & SET_MEMORY_INV) { 16562306a36Sopenharmony_ci new = set_pmd_bit(new, __pgprot(_SEGMENT_ENTRY_INVALID)); 16662306a36Sopenharmony_ci } else if (flags & SET_MEMORY_DEF) { 16762306a36Sopenharmony_ci new = __pmd(pmd_val(new) & PMD_MASK); 16862306a36Sopenharmony_ci new = set_pmd_bit(new, SEGMENT_KERNEL); 16962306a36Sopenharmony_ci if (!MACHINE_HAS_NX) 17062306a36Sopenharmony_ci new = clear_pmd_bit(new, __pgprot(_SEGMENT_ENTRY_NOEXEC)); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci pgt_set((unsigned long *)pmdp, pmd_val(new), addr, CRDTE_DTT_SEGMENT); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic int walk_pmd_level(pud_t *pudp, unsigned long addr, unsigned long end, 17662306a36Sopenharmony_ci unsigned long flags) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci unsigned long next; 17962306a36Sopenharmony_ci int need_split; 18062306a36Sopenharmony_ci pmd_t *pmdp; 18162306a36Sopenharmony_ci int rc = 0; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci pmdp = pmd_offset(pudp, addr); 18462306a36Sopenharmony_ci do { 18562306a36Sopenharmony_ci if (pmd_none(*pmdp)) 18662306a36Sopenharmony_ci return -EINVAL; 18762306a36Sopenharmony_ci next = pmd_addr_end(addr, end); 18862306a36Sopenharmony_ci if (pmd_large(*pmdp)) { 18962306a36Sopenharmony_ci need_split = !!(flags & SET_MEMORY_4K); 19062306a36Sopenharmony_ci need_split |= !!(addr & ~PMD_MASK); 19162306a36Sopenharmony_ci need_split |= !!(addr + PMD_SIZE > next); 19262306a36Sopenharmony_ci if (need_split) { 19362306a36Sopenharmony_ci rc = split_pmd_page(pmdp, addr); 19462306a36Sopenharmony_ci if (rc) 19562306a36Sopenharmony_ci return rc; 19662306a36Sopenharmony_ci continue; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci modify_pmd_page(pmdp, addr, flags); 19962306a36Sopenharmony_ci } else { 20062306a36Sopenharmony_ci rc = walk_pte_level(pmdp, addr, next, flags); 20162306a36Sopenharmony_ci if (rc) 20262306a36Sopenharmony_ci return rc; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci pmdp++; 20562306a36Sopenharmony_ci addr = next; 20662306a36Sopenharmony_ci cond_resched(); 20762306a36Sopenharmony_ci } while (addr < end); 20862306a36Sopenharmony_ci return rc; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic int split_pud_page(pud_t *pudp, unsigned long addr) 21262306a36Sopenharmony_ci{ 21362306a36Sopenharmony_ci unsigned long pmd_addr, prot; 21462306a36Sopenharmony_ci pmd_t *pm_dir, *pmdp; 21562306a36Sopenharmony_ci pud_t new; 21662306a36Sopenharmony_ci int i, ro, nx; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci pm_dir = vmem_crst_alloc(_SEGMENT_ENTRY_EMPTY); 21962306a36Sopenharmony_ci if (!pm_dir) 22062306a36Sopenharmony_ci return -ENOMEM; 22162306a36Sopenharmony_ci pmd_addr = pud_pfn(*pudp) << PAGE_SHIFT; 22262306a36Sopenharmony_ci ro = !!(pud_val(*pudp) & _REGION_ENTRY_PROTECT); 22362306a36Sopenharmony_ci nx = !!(pud_val(*pudp) & _REGION_ENTRY_NOEXEC); 22462306a36Sopenharmony_ci prot = pgprot_val(ro ? SEGMENT_KERNEL_RO : SEGMENT_KERNEL); 22562306a36Sopenharmony_ci if (!nx) 22662306a36Sopenharmony_ci prot &= ~_SEGMENT_ENTRY_NOEXEC; 22762306a36Sopenharmony_ci pmdp = pm_dir; 22862306a36Sopenharmony_ci for (i = 0; i < PTRS_PER_PMD; i++) { 22962306a36Sopenharmony_ci set_pmd(pmdp, __pmd(pmd_addr | prot)); 23062306a36Sopenharmony_ci pmd_addr += PMD_SIZE; 23162306a36Sopenharmony_ci pmdp++; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci new = __pud(__pa(pm_dir) | _REGION3_ENTRY); 23462306a36Sopenharmony_ci pgt_set((unsigned long *)pudp, pud_val(new), addr, CRDTE_DTT_REGION3); 23562306a36Sopenharmony_ci update_page_count(PG_DIRECT_MAP_1M, PTRS_PER_PMD); 23662306a36Sopenharmony_ci update_page_count(PG_DIRECT_MAP_2G, -1); 23762306a36Sopenharmony_ci return 0; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic void modify_pud_page(pud_t *pudp, unsigned long addr, 24162306a36Sopenharmony_ci unsigned long flags) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci pud_t new = *pudp; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci if (flags & SET_MEMORY_RO) 24662306a36Sopenharmony_ci new = pud_wrprotect(new); 24762306a36Sopenharmony_ci else if (flags & SET_MEMORY_RW) 24862306a36Sopenharmony_ci new = pud_mkwrite(pud_mkdirty(new)); 24962306a36Sopenharmony_ci if (flags & SET_MEMORY_NX) 25062306a36Sopenharmony_ci new = set_pud_bit(new, __pgprot(_REGION_ENTRY_NOEXEC)); 25162306a36Sopenharmony_ci else if (flags & SET_MEMORY_X) 25262306a36Sopenharmony_ci new = clear_pud_bit(new, __pgprot(_REGION_ENTRY_NOEXEC)); 25362306a36Sopenharmony_ci if (flags & SET_MEMORY_INV) { 25462306a36Sopenharmony_ci new = set_pud_bit(new, __pgprot(_REGION_ENTRY_INVALID)); 25562306a36Sopenharmony_ci } else if (flags & SET_MEMORY_DEF) { 25662306a36Sopenharmony_ci new = __pud(pud_val(new) & PUD_MASK); 25762306a36Sopenharmony_ci new = set_pud_bit(new, REGION3_KERNEL); 25862306a36Sopenharmony_ci if (!MACHINE_HAS_NX) 25962306a36Sopenharmony_ci new = clear_pud_bit(new, __pgprot(_REGION_ENTRY_NOEXEC)); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci pgt_set((unsigned long *)pudp, pud_val(new), addr, CRDTE_DTT_REGION3); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cistatic int walk_pud_level(p4d_t *p4d, unsigned long addr, unsigned long end, 26562306a36Sopenharmony_ci unsigned long flags) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci unsigned long next; 26862306a36Sopenharmony_ci int need_split; 26962306a36Sopenharmony_ci pud_t *pudp; 27062306a36Sopenharmony_ci int rc = 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci pudp = pud_offset(p4d, addr); 27362306a36Sopenharmony_ci do { 27462306a36Sopenharmony_ci if (pud_none(*pudp)) 27562306a36Sopenharmony_ci return -EINVAL; 27662306a36Sopenharmony_ci next = pud_addr_end(addr, end); 27762306a36Sopenharmony_ci if (pud_large(*pudp)) { 27862306a36Sopenharmony_ci need_split = !!(flags & SET_MEMORY_4K); 27962306a36Sopenharmony_ci need_split |= !!(addr & ~PUD_MASK); 28062306a36Sopenharmony_ci need_split |= !!(addr + PUD_SIZE > next); 28162306a36Sopenharmony_ci if (need_split) { 28262306a36Sopenharmony_ci rc = split_pud_page(pudp, addr); 28362306a36Sopenharmony_ci if (rc) 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci continue; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci modify_pud_page(pudp, addr, flags); 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci rc = walk_pmd_level(pudp, addr, next, flags); 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci pudp++; 29262306a36Sopenharmony_ci addr = next; 29362306a36Sopenharmony_ci cond_resched(); 29462306a36Sopenharmony_ci } while (addr < end && !rc); 29562306a36Sopenharmony_ci return rc; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int walk_p4d_level(pgd_t *pgd, unsigned long addr, unsigned long end, 29962306a36Sopenharmony_ci unsigned long flags) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci unsigned long next; 30262306a36Sopenharmony_ci p4d_t *p4dp; 30362306a36Sopenharmony_ci int rc = 0; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci p4dp = p4d_offset(pgd, addr); 30662306a36Sopenharmony_ci do { 30762306a36Sopenharmony_ci if (p4d_none(*p4dp)) 30862306a36Sopenharmony_ci return -EINVAL; 30962306a36Sopenharmony_ci next = p4d_addr_end(addr, end); 31062306a36Sopenharmony_ci rc = walk_pud_level(p4dp, addr, next, flags); 31162306a36Sopenharmony_ci p4dp++; 31262306a36Sopenharmony_ci addr = next; 31362306a36Sopenharmony_ci cond_resched(); 31462306a36Sopenharmony_ci } while (addr < end && !rc); 31562306a36Sopenharmony_ci return rc; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ciDEFINE_MUTEX(cpa_mutex); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic int change_page_attr(unsigned long addr, unsigned long end, 32162306a36Sopenharmony_ci unsigned long flags) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci unsigned long next; 32462306a36Sopenharmony_ci int rc = -EINVAL; 32562306a36Sopenharmony_ci pgd_t *pgdp; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci pgdp = pgd_offset_k(addr); 32862306a36Sopenharmony_ci do { 32962306a36Sopenharmony_ci if (pgd_none(*pgdp)) 33062306a36Sopenharmony_ci break; 33162306a36Sopenharmony_ci next = pgd_addr_end(addr, end); 33262306a36Sopenharmony_ci rc = walk_p4d_level(pgdp, addr, next, flags); 33362306a36Sopenharmony_ci if (rc) 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci cond_resched(); 33662306a36Sopenharmony_ci } while (pgdp++, addr = next, addr < end && !rc); 33762306a36Sopenharmony_ci return rc; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int change_page_attr_alias(unsigned long addr, unsigned long end, 34162306a36Sopenharmony_ci unsigned long flags) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci unsigned long alias, offset, va_start, va_end; 34462306a36Sopenharmony_ci struct vm_struct *area; 34562306a36Sopenharmony_ci int rc = 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci /* 34862306a36Sopenharmony_ci * Changes to read-only permissions on kernel VA mappings are also 34962306a36Sopenharmony_ci * applied to the kernel direct mapping. Execute permissions are 35062306a36Sopenharmony_ci * intentionally not transferred to keep all allocated pages within 35162306a36Sopenharmony_ci * the direct mapping non-executable. 35262306a36Sopenharmony_ci */ 35362306a36Sopenharmony_ci flags &= SET_MEMORY_RO | SET_MEMORY_RW; 35462306a36Sopenharmony_ci if (!flags) 35562306a36Sopenharmony_ci return 0; 35662306a36Sopenharmony_ci area = NULL; 35762306a36Sopenharmony_ci while (addr < end) { 35862306a36Sopenharmony_ci if (!area) 35962306a36Sopenharmony_ci area = find_vm_area((void *)addr); 36062306a36Sopenharmony_ci if (!area || !(area->flags & VM_ALLOC)) 36162306a36Sopenharmony_ci return 0; 36262306a36Sopenharmony_ci va_start = (unsigned long)area->addr; 36362306a36Sopenharmony_ci va_end = va_start + area->nr_pages * PAGE_SIZE; 36462306a36Sopenharmony_ci offset = (addr - va_start) >> PAGE_SHIFT; 36562306a36Sopenharmony_ci alias = (unsigned long)page_address(area->pages[offset]); 36662306a36Sopenharmony_ci rc = change_page_attr(alias, alias + PAGE_SIZE, flags); 36762306a36Sopenharmony_ci if (rc) 36862306a36Sopenharmony_ci break; 36962306a36Sopenharmony_ci addr += PAGE_SIZE; 37062306a36Sopenharmony_ci if (addr >= va_end) 37162306a36Sopenharmony_ci area = NULL; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci return rc; 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ciint __set_memory(unsigned long addr, unsigned long numpages, unsigned long flags) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci unsigned long end; 37962306a36Sopenharmony_ci int rc; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (!MACHINE_HAS_NX) 38262306a36Sopenharmony_ci flags &= ~(SET_MEMORY_NX | SET_MEMORY_X); 38362306a36Sopenharmony_ci if (!flags) 38462306a36Sopenharmony_ci return 0; 38562306a36Sopenharmony_ci if (!numpages) 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci addr &= PAGE_MASK; 38862306a36Sopenharmony_ci end = addr + numpages * PAGE_SIZE; 38962306a36Sopenharmony_ci mutex_lock(&cpa_mutex); 39062306a36Sopenharmony_ci rc = change_page_attr(addr, end, flags); 39162306a36Sopenharmony_ci if (rc) 39262306a36Sopenharmony_ci goto out; 39362306a36Sopenharmony_ci rc = change_page_attr_alias(addr, end, flags); 39462306a36Sopenharmony_ciout: 39562306a36Sopenharmony_ci mutex_unlock(&cpa_mutex); 39662306a36Sopenharmony_ci return rc; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ciint set_direct_map_invalid_noflush(struct page *page) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci return __set_memory((unsigned long)page_to_virt(page), 1, SET_MEMORY_INV); 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ciint set_direct_map_default_noflush(struct page *page) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci return __set_memory((unsigned long)page_to_virt(page), 1, SET_MEMORY_DEF); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_PAGEALLOC) || defined(CONFIG_KFENCE) 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic void ipte_range(pte_t *pte, unsigned long address, int nr) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci int i; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (test_facility(13)) { 41662306a36Sopenharmony_ci __ptep_ipte_range(address, nr - 1, pte, IPTE_GLOBAL); 41762306a36Sopenharmony_ci return; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci for (i = 0; i < nr; i++) { 42062306a36Sopenharmony_ci __ptep_ipte(address, pte, 0, 0, IPTE_GLOBAL); 42162306a36Sopenharmony_ci address += PAGE_SIZE; 42262306a36Sopenharmony_ci pte++; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_civoid __kernel_map_pages(struct page *page, int numpages, int enable) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci unsigned long address; 42962306a36Sopenharmony_ci pte_t *ptep, pte; 43062306a36Sopenharmony_ci int nr, i, j; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci for (i = 0; i < numpages;) { 43362306a36Sopenharmony_ci address = (unsigned long)page_to_virt(page + i); 43462306a36Sopenharmony_ci ptep = virt_to_kpte(address); 43562306a36Sopenharmony_ci nr = (unsigned long)ptep >> ilog2(sizeof(long)); 43662306a36Sopenharmony_ci nr = PTRS_PER_PTE - (nr & (PTRS_PER_PTE - 1)); 43762306a36Sopenharmony_ci nr = min(numpages - i, nr); 43862306a36Sopenharmony_ci if (enable) { 43962306a36Sopenharmony_ci for (j = 0; j < nr; j++) { 44062306a36Sopenharmony_ci pte = clear_pte_bit(*ptep, __pgprot(_PAGE_INVALID)); 44162306a36Sopenharmony_ci set_pte(ptep, pte); 44262306a36Sopenharmony_ci address += PAGE_SIZE; 44362306a36Sopenharmony_ci ptep++; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci } else { 44662306a36Sopenharmony_ci ipte_range(ptep, address, nr); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci i += nr; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci#endif /* CONFIG_DEBUG_PAGEALLOC */ 453