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> |
305b1523 | 9 | #include <asm/pgtable.h> |
6b70a920 HC |
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 | } | |
305b1523 | 45 | |
5b1ba9e3 HC |
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); | |
18da2369 | 57 | if (pud_none(*pudp) || pud_large(*pudp)) |
5b1ba9e3 HC |
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 | ||
305b1523 JG |
68 | static void change_page_attr(unsigned long addr, int numpages, |
69 | pte_t (*set) (pte_t)) | |
70 | { | |
71 | pte_t *ptep, pte; | |
305b1523 JG |
72 | int i; |
73 | ||
74 | for (i = 0; i < numpages; i++) { | |
5b1ba9e3 HC |
75 | ptep = walk_page_table(addr); |
76 | if (WARN_ON_ONCE(!ptep)) | |
77 | break; | |
305b1523 JG |
78 | pte = *ptep; |
79 | pte = set(pte); | |
b2fa47e6 | 80 | __ptep_ipte(addr, ptep); |
305b1523 | 81 | *ptep = pte; |
e4c031b4 | 82 | addr += PAGE_SIZE; |
305b1523 JG |
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 | } | |
305b1523 JG |
91 | |
92 | int set_memory_rw(unsigned long addr, int numpages) | |
93 | { | |
94 | change_page_attr(addr, numpages, pte_mkwrite); | |
95 | return 0; | |
96 | } | |
305b1523 JG |
97 | |
98 | /* not possible */ | |
99 | int set_memory_nx(unsigned long addr, int numpages) | |
100 | { | |
101 | return 0; | |
102 | } | |
448694a1 JG |
103 | |
104 | int set_memory_x(unsigned long addr, int numpages) | |
105 | { | |
106 | return 0; | |
107 | } | |
0a4ccc99 HC |
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 | } | |
abf09bed | 130 | pte_val(*pte) = __pa(address); |
0a4ccc99 HC |
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 */ |