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