diff options
Diffstat (limited to 'arch/powerpc/platforms/ps3/htab.c')
-rw-r--r-- | arch/powerpc/platforms/ps3/htab.c | 269 |
1 files changed, 91 insertions, 178 deletions
diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index 1cf901fa9031..6eb1d4d182c9 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c | |||
@@ -29,138 +29,75 @@ | |||
29 | 29 | ||
30 | #include "platform.h" | 30 | #include "platform.h" |
31 | 31 | ||
32 | #if defined(DEBUG) | 32 | /** |
33 | #define DBG udbg_printf | 33 | * enum lpar_vas_id - id of LPAR virtual address space. |
34 | #else | 34 | * @lpar_vas_id_current: Current selected virtual address space |
35 | #define DBG pr_debug | 35 | * |
36 | #endif | 36 | * Identify the target LPAR address space. |
37 | 37 | */ | |
38 | static struct hash_pte *htab; | 38 | |
39 | static unsigned long htab_addr; | 39 | enum ps3_lpar_vas_id { |
40 | static unsigned char *bolttab; | 40 | PS3_LPAR_VAS_ID_CURRENT = 0, |
41 | static unsigned char *inusetab; | 41 | }; |
42 | 42 | ||
43 | static DEFINE_SPINLOCK(ps3_bolttab_lock); | 43 | |
44 | 44 | static DEFINE_SPINLOCK(ps3_htab_lock); | |
45 | #define debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g) \ | ||
46 | _debug_dump_hpte(_a, _b, _c, _d, _e, _f, _g, __func__, __LINE__) | ||
47 | static void _debug_dump_hpte(unsigned long pa, unsigned long va, | ||
48 | unsigned long group, unsigned long bitmap, struct hash_pte lhpte, | ||
49 | int psize, unsigned long slot, const char* func, int line) | ||
50 | { | ||
51 | DBG("%s:%d: pa = %lxh\n", func, line, pa); | ||
52 | DBG("%s:%d: lpar = %lxh\n", func, line, | ||
53 | ps3_mm_phys_to_lpar(pa)); | ||
54 | DBG("%s:%d: va = %lxh\n", func, line, va); | ||
55 | DBG("%s:%d: group = %lxh\n", func, line, group); | ||
56 | DBG("%s:%d: bitmap = %lxh\n", func, line, bitmap); | ||
57 | DBG("%s:%d: hpte.v = %lxh\n", func, line, lhpte.v); | ||
58 | DBG("%s:%d: hpte.r = %lxh\n", func, line, lhpte.r); | ||
59 | DBG("%s:%d: psize = %xh\n", func, line, psize); | ||
60 | DBG("%s:%d: slot = %lxh\n", func, line, slot); | ||
61 | } | ||
62 | 45 | ||
63 | static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va, | 46 | static long ps3_hpte_insert(unsigned long hpte_group, unsigned long va, |
64 | unsigned long pa, unsigned long rflags, unsigned long vflags, | 47 | unsigned long pa, unsigned long rflags, unsigned long vflags, |
65 | int psize, int ssize) | 48 | int psize, int ssize) |
66 | { | 49 | { |
67 | unsigned long slot; | 50 | int result; |
68 | struct hash_pte lhpte; | 51 | u64 hpte_v, hpte_r; |
69 | int secondary = 0; | 52 | u64 inserted_index; |
70 | unsigned long result; | 53 | u64 evicted_v, evicted_r; |
71 | unsigned long bitmap; | 54 | u64 hpte_v_array[4], hpte_rs; |
72 | unsigned long flags; | 55 | unsigned long flags; |
73 | unsigned long p_pteg, s_pteg, b_index, b_mask, cb, ci; | 56 | long ret = -1; |
74 | |||
75 | vflags &= ~HPTE_V_SECONDARY; /* this bit is ignored */ | ||
76 | |||
77 | lhpte.v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M) | | ||
78 | vflags | HPTE_V_VALID; | ||
79 | lhpte.r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize) | rflags; | ||
80 | |||
81 | p_pteg = hpte_group / HPTES_PER_GROUP; | ||
82 | s_pteg = ~p_pteg & htab_hash_mask; | ||
83 | |||
84 | spin_lock_irqsave(&ps3_bolttab_lock, flags); | ||
85 | |||
86 | BUG_ON(bolttab[p_pteg] == 0xff && bolttab[s_pteg] == 0xff); | ||
87 | 57 | ||
88 | bitmap = (inusetab[p_pteg] << 8) | inusetab[s_pteg]; | 58 | /* |
59 | * lv1_insert_htab_entry() will search for victim | ||
60 | * entry in both primary and secondary pte group | ||
61 | */ | ||
62 | vflags &= ~HPTE_V_SECONDARY; | ||
89 | 63 | ||
90 | if (bitmap == 0xffff) { | 64 | hpte_v = hpte_encode_v(va, psize, ssize) | vflags | HPTE_V_VALID; |
91 | /* | 65 | hpte_r = hpte_encode_r(ps3_mm_phys_to_lpar(pa), psize) | rflags; |
92 | * PTEG is full. Search for victim. | ||
93 | */ | ||
94 | bitmap &= ~((bolttab[p_pteg] << 8) | bolttab[s_pteg]); | ||
95 | do { | ||
96 | ci = mftb() & 15; | ||
97 | cb = 0x8000UL >> ci; | ||
98 | } while ((cb & bitmap) == 0); | ||
99 | } else { | ||
100 | /* | ||
101 | * search free slot in hardware order | ||
102 | * [primary] 0, 2, 4, 6, 1, 3, 5, 7 | ||
103 | * [secondary] 0, 2, 4, 6, 1, 3, 5, 7 | ||
104 | */ | ||
105 | for (ci = 0; ci < HPTES_PER_GROUP; ci += 2) { | ||
106 | cb = 0x8000UL >> ci; | ||
107 | if ((cb & bitmap) == 0) | ||
108 | goto found; | ||
109 | } | ||
110 | for (ci = 1; ci < HPTES_PER_GROUP; ci += 2) { | ||
111 | cb = 0x8000UL >> ci; | ||
112 | if ((cb & bitmap) == 0) | ||
113 | goto found; | ||
114 | } | ||
115 | for (ci = HPTES_PER_GROUP; ci < HPTES_PER_GROUP*2; ci += 2) { | ||
116 | cb = 0x8000UL >> ci; | ||
117 | if ((cb & bitmap) == 0) | ||
118 | goto found; | ||
119 | } | ||
120 | for (ci = HPTES_PER_GROUP+1; ci < HPTES_PER_GROUP*2; ci += 2) { | ||
121 | cb = 0x8000UL >> ci; | ||
122 | if ((cb & bitmap) == 0) | ||
123 | goto found; | ||
124 | } | ||
125 | } | ||
126 | 66 | ||
127 | found: | 67 | spin_lock_irqsave(&ps3_htab_lock, flags); |
128 | if (ci < HPTES_PER_GROUP) { | ||
129 | slot = p_pteg * HPTES_PER_GROUP + ci; | ||
130 | } else { | ||
131 | slot = s_pteg * HPTES_PER_GROUP + (ci & 7); | ||
132 | /* lhpte.dw0.dw0.h = 1; */ | ||
133 | vflags |= HPTE_V_SECONDARY; | ||
134 | lhpte.v |= HPTE_V_SECONDARY; | ||
135 | } | ||
136 | 68 | ||
137 | result = lv1_write_htab_entry(0, slot, lhpte.v, lhpte.r); | 69 | /* talk hvc to replace entries BOLTED == 0 */ |
70 | result = lv1_insert_htab_entry(PS3_LPAR_VAS_ID_CURRENT, hpte_group, | ||
71 | hpte_v, hpte_r, | ||
72 | HPTE_V_BOLTED, 0, | ||
73 | &inserted_index, | ||
74 | &evicted_v, &evicted_r); | ||
138 | 75 | ||
139 | if (result) { | 76 | if (result) { |
140 | debug_dump_hpte(pa, va, hpte_group, bitmap, lhpte, psize, slot); | 77 | /* all entries bolted !*/ |
78 | pr_info("%s:result=%d va=%lx pa=%lx ix=%lx v=%lx r=%lx\n", | ||
79 | __func__, result, va, pa, hpte_group, hpte_v, hpte_r); | ||
141 | BUG(); | 80 | BUG(); |
142 | } | 81 | } |
143 | 82 | ||
144 | /* | 83 | /* |
145 | * If used slot is not in primary HPTE group, | 84 | * see if the entry is inserted into secondary pteg |
146 | * the slot should be in secondary HPTE group. | ||
147 | */ | 85 | */ |
86 | result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, | ||
87 | inserted_index & ~0x3UL, | ||
88 | &hpte_v_array[0], &hpte_v_array[1], | ||
89 | &hpte_v_array[2], &hpte_v_array[3], | ||
90 | &hpte_rs); | ||
91 | BUG_ON(result); | ||
148 | 92 | ||
149 | if ((hpte_group ^ slot) & ~(HPTES_PER_GROUP - 1)) { | 93 | if (hpte_v_array[inserted_index % 4] & HPTE_V_SECONDARY) |
150 | secondary = 1; | 94 | ret = (inserted_index & 7) | (1 << 3); |
151 | b_index = s_pteg; | 95 | else |
152 | } else { | 96 | ret = inserted_index & 7; |
153 | secondary = 0; | ||
154 | b_index = p_pteg; | ||
155 | } | ||
156 | 97 | ||
157 | b_mask = (lhpte.v & HPTE_V_BOLTED) ? 1 << 7 : 0 << 7; | 98 | spin_unlock_irqrestore(&ps3_htab_lock, flags); |
158 | bolttab[b_index] |= b_mask >> (slot & 7); | ||
159 | b_mask = 1 << 7; | ||
160 | inusetab[b_index] |= b_mask >> (slot & 7); | ||
161 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | ||
162 | 99 | ||
163 | return (slot & 7) | (secondary << 3); | 100 | return ret; |
164 | } | 101 | } |
165 | 102 | ||
166 | static long ps3_hpte_remove(unsigned long hpte_group) | 103 | static long ps3_hpte_remove(unsigned long hpte_group) |
@@ -172,39 +109,48 @@ static long ps3_hpte_remove(unsigned long hpte_group) | |||
172 | static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp, | 109 | static long ps3_hpte_updatepp(unsigned long slot, unsigned long newpp, |
173 | unsigned long va, int psize, int ssize, int local) | 110 | unsigned long va, int psize, int ssize, int local) |
174 | { | 111 | { |
112 | int result; | ||
113 | u64 hpte_v, want_v, hpte_rs; | ||
114 | u64 hpte_v_array[4]; | ||
175 | unsigned long flags; | 115 | unsigned long flags; |
176 | unsigned long result; | 116 | long ret; |
177 | unsigned long pteg, bit; | ||
178 | unsigned long hpte_v, want_v; | ||
179 | 117 | ||
180 | want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M); | 118 | want_v = hpte_encode_v(va, psize, ssize); |
181 | 119 | ||
182 | spin_lock_irqsave(&ps3_bolttab_lock, flags); | 120 | spin_lock_irqsave(&ps3_htab_lock, flags); |
183 | 121 | ||
184 | hpte_v = htab[slot].v; | 122 | result = lv1_read_htab_entries(PS3_LPAR_VAS_ID_CURRENT, slot & ~0x3UL, |
185 | if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { | 123 | &hpte_v_array[0], &hpte_v_array[1], |
186 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | 124 | &hpte_v_array[2], &hpte_v_array[3], |
187 | 125 | &hpte_rs); | |
188 | /* ps3_hpte_insert() will be used to update PTE */ | ||
189 | return -1; | ||
190 | } | ||
191 | |||
192 | result = lv1_write_htab_entry(0, slot, 0, 0); | ||
193 | 126 | ||
194 | if (result) { | 127 | if (result) { |
195 | DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", | 128 | pr_info("%s: res=%d read va=%lx slot=%lx psize=%d\n", |
196 | __func__, va, slot, psize, result, result); | 129 | __func__, result, va, slot, psize); |
197 | BUG(); | 130 | BUG(); |
198 | } | 131 | } |
199 | 132 | ||
200 | pteg = slot / HPTES_PER_GROUP; | 133 | hpte_v = hpte_v_array[slot % 4]; |
201 | bit = slot % HPTES_PER_GROUP; | ||
202 | inusetab[pteg] &= ~(0x80 >> bit); | ||
203 | 134 | ||
204 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | 135 | /* |
136 | * As lv1_read_htab_entries() does not give us the RPN, we can | ||
137 | * not synthesize the new hpte_r value here, and therefore can | ||
138 | * not update the hpte with lv1_insert_htab_entry(), so we | ||
139 | * insted invalidate it and ask the caller to update it via | ||
140 | * ps3_hpte_insert() by returning a -1 value. | ||
141 | */ | ||
142 | if (!HPTE_V_COMPARE(hpte_v, want_v) || !(hpte_v & HPTE_V_VALID)) { | ||
143 | /* not found */ | ||
144 | ret = -1; | ||
145 | } else { | ||
146 | /* entry found, just invalidate it */ | ||
147 | result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, | ||
148 | slot, 0, 0); | ||
149 | ret = -1; | ||
150 | } | ||
205 | 151 | ||
206 | /* ps3_hpte_insert() will be used to update PTE */ | 152 | spin_unlock_irqrestore(&ps3_htab_lock, flags); |
207 | return -1; | 153 | return ret; |
208 | } | 154 | } |
209 | 155 | ||
210 | static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, | 156 | static void ps3_hpte_updateboltedpp(unsigned long newpp, unsigned long ea, |
@@ -217,45 +163,35 @@ static void ps3_hpte_invalidate(unsigned long slot, unsigned long va, | |||
217 | int psize, int ssize, int local) | 163 | int psize, int ssize, int local) |
218 | { | 164 | { |
219 | unsigned long flags; | 165 | unsigned long flags; |
220 | unsigned long result; | 166 | int result; |
221 | unsigned long pteg, bit; | 167 | |
168 | spin_lock_irqsave(&ps3_htab_lock, flags); | ||
222 | 169 | ||
223 | spin_lock_irqsave(&ps3_bolttab_lock, flags); | 170 | result = lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, slot, 0, 0); |
224 | result = lv1_write_htab_entry(0, slot, 0, 0); | ||
225 | 171 | ||
226 | if (result) { | 172 | if (result) { |
227 | DBG("%s: va=%lx slot=%lx psize=%d result = %ld (0x%lx)\n", | 173 | pr_info("%s: res=%d va=%lx slot=%lx psize=%d\n", |
228 | __func__, va, slot, psize, result, result); | 174 | __func__, result, va, slot, psize); |
229 | BUG(); | 175 | BUG(); |
230 | } | 176 | } |
231 | 177 | ||
232 | pteg = slot / HPTES_PER_GROUP; | 178 | spin_unlock_irqrestore(&ps3_htab_lock, flags); |
233 | bit = slot % HPTES_PER_GROUP; | ||
234 | inusetab[pteg] &= ~(0x80 >> bit); | ||
235 | spin_unlock_irqrestore(&ps3_bolttab_lock, flags); | ||
236 | } | 179 | } |
237 | 180 | ||
238 | static void ps3_hpte_clear(void) | 181 | static void ps3_hpte_clear(void) |
239 | { | 182 | { |
240 | int result; | 183 | unsigned long hpte_count = (1UL << ppc64_pft_size) >> 4; |
241 | 184 | u64 i; | |
242 | DBG(" -> %s:%d\n", __func__, __LINE__); | ||
243 | 185 | ||
244 | result = lv1_unmap_htab(htab_addr); | 186 | for (i = 0; i < hpte_count; i++) |
245 | BUG_ON(result); | 187 | lv1_write_htab_entry(PS3_LPAR_VAS_ID_CURRENT, i, 0, 0); |
246 | 188 | ||
247 | ps3_mm_shutdown(); | 189 | ps3_mm_shutdown(); |
248 | ps3_mm_vas_destroy(); | 190 | ps3_mm_vas_destroy(); |
249 | |||
250 | DBG(" <- %s:%d\n", __func__, __LINE__); | ||
251 | } | 191 | } |
252 | 192 | ||
253 | void __init ps3_hpte_init(unsigned long htab_size) | 193 | void __init ps3_hpte_init(unsigned long htab_size) |
254 | { | 194 | { |
255 | long bitmap_size; | ||
256 | |||
257 | DBG(" -> %s:%d\n", __func__, __LINE__); | ||
258 | |||
259 | ppc_md.hpte_invalidate = ps3_hpte_invalidate; | 195 | ppc_md.hpte_invalidate = ps3_hpte_invalidate; |
260 | ppc_md.hpte_updatepp = ps3_hpte_updatepp; | 196 | ppc_md.hpte_updatepp = ps3_hpte_updatepp; |
261 | ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp; | 197 | ppc_md.hpte_updateboltedpp = ps3_hpte_updateboltedpp; |
@@ -264,28 +200,5 @@ void __init ps3_hpte_init(unsigned long htab_size) | |||
264 | ppc_md.hpte_clear_all = ps3_hpte_clear; | 200 | ppc_md.hpte_clear_all = ps3_hpte_clear; |
265 | 201 | ||
266 | ppc64_pft_size = __ilog2(htab_size); | 202 | ppc64_pft_size = __ilog2(htab_size); |
267 | |||
268 | bitmap_size = htab_size / sizeof(struct hash_pte) / 8; | ||
269 | |||
270 | bolttab = __va(lmb_alloc(bitmap_size, 1)); | ||
271 | inusetab = __va(lmb_alloc(bitmap_size, 1)); | ||
272 | |||
273 | memset(bolttab, 0, bitmap_size); | ||
274 | memset(inusetab, 0, bitmap_size); | ||
275 | |||
276 | DBG(" <- %s:%d\n", __func__, __LINE__); | ||
277 | } | 203 | } |
278 | 204 | ||
279 | void __init ps3_map_htab(void) | ||
280 | { | ||
281 | long result; | ||
282 | unsigned long htab_size = (1UL << ppc64_pft_size); | ||
283 | |||
284 | result = lv1_map_htab(0, &htab_addr); | ||
285 | |||
286 | htab = (__force struct hash_pte *)ioremap_flags(htab_addr, htab_size, | ||
287 | pgprot_val(PAGE_READONLY_X)); | ||
288 | |||
289 | DBG("%s:%d: lpar %016lxh, virt %016lxh\n", __func__, __LINE__, | ||
290 | htab_addr, (unsigned long)htab); | ||
291 | } | ||