1 /* 2 * Copyright IBM Corp. 2011 3 * Author(s): Jan Glauber <jang@linux.vnet.ibm.com> 4 */ 5 #include <linux/hugetlb.h> 6 #include <linux/module.h> 7 #include <linux/mm.h> 8 #include <asm/cacheflush.h> 9 #include <asm/pgtable.h> 10 #include <asm/page.h> 11 12 #if PAGE_DEFAULT_KEY 13 static inline unsigned long sske_frame(unsigned long addr, unsigned char skey) 14 { 15 asm volatile(".insn rrf,0xb22b0000,%[skey],%[addr],9,0" 16 : [addr] "+a" (addr) : [skey] "d" (skey)); 17 return addr; 18 } 19 20 void __storage_key_init_range(unsigned long start, unsigned long end) 21 { 22 unsigned long boundary, size; 23 24 while (start < end) { 25 if (MACHINE_HAS_EDAT1) { 26 /* set storage keys for a 1MB frame */ 27 size = 1UL << 20; 28 boundary = (start + size) & ~(size - 1); 29 if (boundary <= end) { 30 do { 31 start = sske_frame(start, PAGE_DEFAULT_KEY); 32 } while (start < boundary); 33 continue; 34 } 35 } 36 page_set_storage_key(start, PAGE_DEFAULT_KEY, 0); 37 start += PAGE_SIZE; 38 } 39 } 40 #endif 41 42 static pte_t *walk_page_table(unsigned long addr) 43 { 44 pgd_t *pgdp; 45 pud_t *pudp; 46 pmd_t *pmdp; 47 pte_t *ptep; 48 49 pgdp = pgd_offset_k(addr); 50 if (pgd_none(*pgdp)) 51 return NULL; 52 pudp = pud_offset(pgdp, addr); 53 if (pud_none(*pudp) || pud_large(*pudp)) 54 return NULL; 55 pmdp = pmd_offset(pudp, addr); 56 if (pmd_none(*pmdp) || pmd_large(*pmdp)) 57 return NULL; 58 ptep = pte_offset_kernel(pmdp, addr); 59 if (pte_none(*ptep)) 60 return NULL; 61 return ptep; 62 } 63 64 static void change_page_attr(unsigned long addr, int numpages, 65 pte_t (*set) (pte_t)) 66 { 67 pte_t *ptep, pte; 68 int i; 69 70 for (i = 0; i < numpages; i++) { 71 ptep = walk_page_table(addr); 72 if (WARN_ON_ONCE(!ptep)) 73 break; 74 pte = *ptep; 75 pte = set(pte); 76 __ptep_ipte(addr, ptep); 77 *ptep = pte; 78 addr += PAGE_SIZE; 79 } 80 } 81 82 int set_memory_ro(unsigned long addr, int numpages) 83 { 84 change_page_attr(addr, numpages, pte_wrprotect); 85 return 0; 86 } 87 88 int set_memory_rw(unsigned long addr, int numpages) 89 { 90 change_page_attr(addr, numpages, pte_mkwrite); 91 return 0; 92 } 93 94 /* not possible */ 95 int set_memory_nx(unsigned long addr, int numpages) 96 { 97 return 0; 98 } 99 100 int set_memory_x(unsigned long addr, int numpages) 101 { 102 return 0; 103 } 104 105 #ifdef CONFIG_DEBUG_PAGEALLOC 106 void kernel_map_pages(struct page *page, int numpages, int enable) 107 { 108 unsigned long address; 109 pgd_t *pgd; 110 pud_t *pud; 111 pmd_t *pmd; 112 pte_t *pte; 113 int i; 114 115 for (i = 0; i < numpages; i++) { 116 address = page_to_phys(page + i); 117 pgd = pgd_offset_k(address); 118 pud = pud_offset(pgd, address); 119 pmd = pmd_offset(pud, address); 120 pte = pte_offset_kernel(pmd, address); 121 if (!enable) { 122 __ptep_ipte(address, pte); 123 pte_val(*pte) = _PAGE_INVALID; 124 continue; 125 } 126 pte_val(*pte) = __pa(address); 127 } 128 } 129 130 #ifdef CONFIG_HIBERNATION 131 bool kernel_page_present(struct page *page) 132 { 133 unsigned long addr; 134 int cc; 135 136 addr = page_to_phys(page); 137 asm volatile( 138 " lra %1,0(%1)\n" 139 " ipm %0\n" 140 " srl %0,28" 141 : "=d" (cc), "+a" (addr) : : "cc"); 142 return cc == 0; 143 } 144 #endif /* CONFIG_HIBERNATION */ 145 146 #endif /* CONFIG_DEBUG_PAGEALLOC */ 147