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