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