1 /*
2  * "Cell Reference Set" HTAB support.
3  *
4  * (C) Copyright 2006-2007 TOSHIBA CORPORATION
5  *
6  * This code is based on arch/powerpc/platforms/pseries/lpar.c:
7  *  Copyright (C) 2001 Todd Inglett, IBM Corporation
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #undef DEBUG_LOW
25 
26 #include <linux/kernel.h>
27 #include <linux/spinlock.h>
28 
29 #include <asm/mmu.h>
30 #include <asm/page.h>
31 #include <asm/pgtable.h>
32 #include <asm/machdep.h>
33 #include <asm/udbg.h>
34 
35 #include "beat_wrapper.h"
36 
37 #ifdef DEBUG_LOW
38 #define DBG_LOW(fmt...) do { udbg_printf(fmt); } while (0)
39 #else
40 #define DBG_LOW(fmt...) do { } while (0)
41 #endif
42 
43 static DEFINE_RAW_SPINLOCK(beat_htab_lock);
44 
beat_read_mask(unsigned hpte_group)45 static inline unsigned int beat_read_mask(unsigned hpte_group)
46 {
47 	unsigned long rmask = 0;
48 	u64 hpte_v[5];
49 
50 	beat_read_htab_entries(0, hpte_group + 0, hpte_v);
51 	if (!(hpte_v[0] & HPTE_V_BOLTED))
52 		rmask |= 0x8000;
53 	if (!(hpte_v[1] & HPTE_V_BOLTED))
54 		rmask |= 0x4000;
55 	if (!(hpte_v[2] & HPTE_V_BOLTED))
56 		rmask |= 0x2000;
57 	if (!(hpte_v[3] & HPTE_V_BOLTED))
58 		rmask |= 0x1000;
59 	beat_read_htab_entries(0, hpte_group + 4, hpte_v);
60 	if (!(hpte_v[0] & HPTE_V_BOLTED))
61 		rmask |= 0x0800;
62 	if (!(hpte_v[1] & HPTE_V_BOLTED))
63 		rmask |= 0x0400;
64 	if (!(hpte_v[2] & HPTE_V_BOLTED))
65 		rmask |= 0x0200;
66 	if (!(hpte_v[3] & HPTE_V_BOLTED))
67 		rmask |= 0x0100;
68 	hpte_group = ~hpte_group & (htab_hash_mask * HPTES_PER_GROUP);
69 	beat_read_htab_entries(0, hpte_group + 0, hpte_v);
70 	if (!(hpte_v[0] & HPTE_V_BOLTED))
71 		rmask |= 0x80;
72 	if (!(hpte_v[1] & HPTE_V_BOLTED))
73 		rmask |= 0x40;
74 	if (!(hpte_v[2] & HPTE_V_BOLTED))
75 		rmask |= 0x20;
76 	if (!(hpte_v[3] & HPTE_V_BOLTED))
77 		rmask |= 0x10;
78 	beat_read_htab_entries(0, hpte_group + 4, hpte_v);
79 	if (!(hpte_v[0] & HPTE_V_BOLTED))
80 		rmask |= 0x08;
81 	if (!(hpte_v[1] & HPTE_V_BOLTED))
82 		rmask |= 0x04;
83 	if (!(hpte_v[2] & HPTE_V_BOLTED))
84 		rmask |= 0x02;
85 	if (!(hpte_v[3] & HPTE_V_BOLTED))
86 		rmask |= 0x01;
87 	return rmask;
88 }
89 
beat_lpar_hpte_insert(unsigned long hpte_group,unsigned long va,unsigned long pa,unsigned long rflags,unsigned long vflags,int psize,int ssize)90 static long beat_lpar_hpte_insert(unsigned long hpte_group,
91 				  unsigned long va, unsigned long pa,
92 				  unsigned long rflags, unsigned long vflags,
93 				  int psize, int ssize)
94 {
95 	unsigned long lpar_rc;
96 	u64 hpte_v, hpte_r, slot;
97 
98 	if (vflags & HPTE_V_SECONDARY)
99 		return -1;
100 
101 	if (!(vflags & HPTE_V_BOLTED))
102 		DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, "
103 			"rflags=%lx, vflags=%lx, psize=%d)\n",
104 		hpte_group, va, pa, rflags, vflags, psize);
105 
106 	hpte_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M) |
107 		vflags | HPTE_V_VALID;
108 	hpte_r = hpte_encode_r(pa, psize) | rflags;
109 
110 	if (!(vflags & HPTE_V_BOLTED))
111 		DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);
112 
113 	if (rflags & _PAGE_NO_CACHE)
114 		hpte_r &= ~_PAGE_COHERENT;
115 
116 	raw_spin_lock(&beat_htab_lock);
117 	lpar_rc = beat_read_mask(hpte_group);
118 	if (lpar_rc == 0) {
119 		if (!(vflags & HPTE_V_BOLTED))
120 			DBG_LOW(" full\n");
121 		raw_spin_unlock(&beat_htab_lock);
122 		return -1;
123 	}
124 
125 	lpar_rc = beat_insert_htab_entry(0, hpte_group, lpar_rc << 48,
126 		hpte_v, hpte_r, &slot);
127 	raw_spin_unlock(&beat_htab_lock);
128 
129 	/*
130 	 * Since we try and ioremap PHBs we don't own, the pte insert
131 	 * will fail. However we must catch the failure in hash_page
132 	 * or we will loop forever, so return -2 in this case.
133 	 */
134 	if (unlikely(lpar_rc != 0)) {
135 		if (!(vflags & HPTE_V_BOLTED))
136 			DBG_LOW(" lpar err %lx\n", lpar_rc);
137 		return -2;
138 	}
139 	if (!(vflags & HPTE_V_BOLTED))
140 		DBG_LOW(" -> slot: %lx\n", slot);
141 
142 	/* We have to pass down the secondary bucket bit here as well */
143 	return (slot ^ hpte_group) & 15;
144 }
145 
beat_lpar_hpte_remove(unsigned long hpte_group)146 static long beat_lpar_hpte_remove(unsigned long hpte_group)
147 {
148 	DBG_LOW("hpte_remove(group=%lx)\n", hpte_group);
149 	return -1;
150 }
151 
beat_lpar_hpte_getword0(unsigned long slot)152 static unsigned long beat_lpar_hpte_getword0(unsigned long slot)
153 {
154 	unsigned long dword0;
155 	unsigned long lpar_rc;
156 	u64 dword[5];
157 
158 	lpar_rc = beat_read_htab_entries(0, slot & ~3UL, dword);
159 
160 	dword0 = dword[slot&3];
161 
162 	BUG_ON(lpar_rc != 0);
163 
164 	return dword0;
165 }
166 
beat_lpar_hptab_clear(void)167 static void beat_lpar_hptab_clear(void)
168 {
169 	unsigned long size_bytes = 1UL << ppc64_pft_size;
170 	unsigned long hpte_count = size_bytes >> 4;
171 	int i;
172 	u64 dummy0, dummy1;
173 
174 	/* TODO: Use bulk call */
175 	for (i = 0; i < hpte_count; i++)
176 		beat_write_htab_entry(0, i, 0, 0, -1UL, -1UL, &dummy0, &dummy1);
177 }
178 
179 /*
180  * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and
181  * the low 3 bits of flags happen to line up.  So no transform is needed.
182  * We can probably optimize here and assume the high bits of newpp are
183  * already zero.  For now I am paranoid.
184  */
beat_lpar_hpte_updatepp(unsigned long slot,unsigned long newpp,unsigned long va,int psize,int ssize,int local)185 static long beat_lpar_hpte_updatepp(unsigned long slot,
186 				    unsigned long newpp,
187 				    unsigned long va,
188 				    int psize, int ssize, int local)
189 {
190 	unsigned long lpar_rc;
191 	u64 dummy0, dummy1;
192 	unsigned long want_v;
193 
194 	want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);
195 
196 	DBG_LOW("    update: "
197 		"avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ",
198 		want_v & HPTE_V_AVPN, slot, psize, newpp);
199 
200 	raw_spin_lock(&beat_htab_lock);
201 	dummy0 = beat_lpar_hpte_getword0(slot);
202 	if ((dummy0 & ~0x7FUL) != (want_v & ~0x7FUL)) {
203 		DBG_LOW("not found !\n");
204 		raw_spin_unlock(&beat_htab_lock);
205 		return -1;
206 	}
207 
208 	lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7, &dummy0,
209 					&dummy1);
210 	raw_spin_unlock(&beat_htab_lock);
211 	if (lpar_rc != 0 || dummy0 == 0) {
212 		DBG_LOW("not found !\n");
213 		return -1;
214 	}
215 
216 	DBG_LOW("ok %lx %lx\n", dummy0, dummy1);
217 
218 	BUG_ON(lpar_rc != 0);
219 
220 	return 0;
221 }
222 
beat_lpar_hpte_find(unsigned long va,int psize)223 static long beat_lpar_hpte_find(unsigned long va, int psize)
224 {
225 	unsigned long hash;
226 	unsigned long i, j;
227 	long slot;
228 	unsigned long want_v, hpte_v;
229 
230 	hash = hpt_hash(va, mmu_psize_defs[psize].shift, MMU_SEGSIZE_256M);
231 	want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);
232 
233 	for (j = 0; j < 2; j++) {
234 		slot = (hash & htab_hash_mask) * HPTES_PER_GROUP;
235 		for (i = 0; i < HPTES_PER_GROUP; i++) {
236 			hpte_v = beat_lpar_hpte_getword0(slot);
237 
238 			if (HPTE_V_COMPARE(hpte_v, want_v)
239 			    && (hpte_v & HPTE_V_VALID)
240 			    && (!!(hpte_v & HPTE_V_SECONDARY) == j)) {
241 				/* HPTE matches */
242 				if (j)
243 					slot = -slot;
244 				return slot;
245 			}
246 			++slot;
247 		}
248 		hash = ~hash;
249 	}
250 
251 	return -1;
252 }
253 
beat_lpar_hpte_updateboltedpp(unsigned long newpp,unsigned long ea,int psize,int ssize)254 static void beat_lpar_hpte_updateboltedpp(unsigned long newpp,
255 					  unsigned long ea,
256 					  int psize, int ssize)
257 {
258 	unsigned long lpar_rc, slot, vsid, va;
259 	u64 dummy0, dummy1;
260 
261 	vsid = get_kernel_vsid(ea, MMU_SEGSIZE_256M);
262 	va = (vsid << 28) | (ea & 0x0fffffff);
263 
264 	raw_spin_lock(&beat_htab_lock);
265 	slot = beat_lpar_hpte_find(va, psize);
266 	BUG_ON(slot == -1);
267 
268 	lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7,
269 		&dummy0, &dummy1);
270 	raw_spin_unlock(&beat_htab_lock);
271 
272 	BUG_ON(lpar_rc != 0);
273 }
274 
beat_lpar_hpte_invalidate(unsigned long slot,unsigned long va,int psize,int ssize,int local)275 static void beat_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
276 					 int psize, int ssize, int local)
277 {
278 	unsigned long want_v;
279 	unsigned long lpar_rc;
280 	u64 dummy1, dummy2;
281 	unsigned long flags;
282 
283 	DBG_LOW("    inval : slot=%lx, va=%016lx, psize: %d, local: %d\n",
284 		slot, va, psize, local);
285 	want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);
286 
287 	raw_spin_lock_irqsave(&beat_htab_lock, flags);
288 	dummy1 = beat_lpar_hpte_getword0(slot);
289 
290 	if ((dummy1 & ~0x7FUL) != (want_v & ~0x7FUL)) {
291 		DBG_LOW("not found !\n");
292 		raw_spin_unlock_irqrestore(&beat_htab_lock, flags);
293 		return;
294 	}
295 
296 	lpar_rc = beat_write_htab_entry(0, slot, 0, 0, HPTE_V_VALID, 0,
297 		&dummy1, &dummy2);
298 	raw_spin_unlock_irqrestore(&beat_htab_lock, flags);
299 
300 	BUG_ON(lpar_rc != 0);
301 }
302 
hpte_init_beat(void)303 void __init hpte_init_beat(void)
304 {
305 	ppc_md.hpte_invalidate	= beat_lpar_hpte_invalidate;
306 	ppc_md.hpte_updatepp	= beat_lpar_hpte_updatepp;
307 	ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;
308 	ppc_md.hpte_insert	= beat_lpar_hpte_insert;
309 	ppc_md.hpte_remove	= beat_lpar_hpte_remove;
310 	ppc_md.hpte_clear_all	= beat_lpar_hptab_clear;
311 }
312 
beat_lpar_hpte_insert_v3(unsigned long hpte_group,unsigned long va,unsigned long pa,unsigned long rflags,unsigned long vflags,int psize,int ssize)313 static long beat_lpar_hpte_insert_v3(unsigned long hpte_group,
314 				  unsigned long va, unsigned long pa,
315 				  unsigned long rflags, unsigned long vflags,
316 				  int psize, int ssize)
317 {
318 	unsigned long lpar_rc;
319 	u64 hpte_v, hpte_r, slot;
320 
321 	if (vflags & HPTE_V_SECONDARY)
322 		return -1;
323 
324 	if (!(vflags & HPTE_V_BOLTED))
325 		DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, "
326 			"rflags=%lx, vflags=%lx, psize=%d)\n",
327 		hpte_group, va, pa, rflags, vflags, psize);
328 
329 	hpte_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M) |
330 		vflags | HPTE_V_VALID;
331 	hpte_r = hpte_encode_r(pa, psize) | rflags;
332 
333 	if (!(vflags & HPTE_V_BOLTED))
334 		DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r);
335 
336 	if (rflags & _PAGE_NO_CACHE)
337 		hpte_r &= ~_PAGE_COHERENT;
338 
339 	/* insert into not-volted entry */
340 	lpar_rc = beat_insert_htab_entry3(0, hpte_group, hpte_v, hpte_r,
341 		HPTE_V_BOLTED, 0, &slot);
342 	/*
343 	 * Since we try and ioremap PHBs we don't own, the pte insert
344 	 * will fail. However we must catch the failure in hash_page
345 	 * or we will loop forever, so return -2 in this case.
346 	 */
347 	if (unlikely(lpar_rc != 0)) {
348 		if (!(vflags & HPTE_V_BOLTED))
349 			DBG_LOW(" lpar err %lx\n", lpar_rc);
350 		return -2;
351 	}
352 	if (!(vflags & HPTE_V_BOLTED))
353 		DBG_LOW(" -> slot: %lx\n", slot);
354 
355 	/* We have to pass down the secondary bucket bit here as well */
356 	return (slot ^ hpte_group) & 15;
357 }
358 
359 /*
360  * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and
361  * the low 3 bits of flags happen to line up.  So no transform is needed.
362  * We can probably optimize here and assume the high bits of newpp are
363  * already zero.  For now I am paranoid.
364  */
beat_lpar_hpte_updatepp_v3(unsigned long slot,unsigned long newpp,unsigned long va,int psize,int ssize,int local)365 static long beat_lpar_hpte_updatepp_v3(unsigned long slot,
366 				    unsigned long newpp,
367 				    unsigned long va,
368 				    int psize, int ssize, int local)
369 {
370 	unsigned long lpar_rc;
371 	unsigned long want_v;
372 	unsigned long pss;
373 
374 	want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);
375 	pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc;
376 
377 	DBG_LOW("    update: "
378 		"avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ",
379 		want_v & HPTE_V_AVPN, slot, psize, newpp);
380 
381 	lpar_rc = beat_update_htab_permission3(0, slot, want_v, pss, 7, newpp);
382 
383 	if (lpar_rc == 0xfffffff7) {
384 		DBG_LOW("not found !\n");
385 		return -1;
386 	}
387 
388 	DBG_LOW("ok\n");
389 
390 	BUG_ON(lpar_rc != 0);
391 
392 	return 0;
393 }
394 
beat_lpar_hpte_invalidate_v3(unsigned long slot,unsigned long va,int psize,int ssize,int local)395 static void beat_lpar_hpte_invalidate_v3(unsigned long slot, unsigned long va,
396 					 int psize, int ssize, int local)
397 {
398 	unsigned long want_v;
399 	unsigned long lpar_rc;
400 	unsigned long pss;
401 
402 	DBG_LOW("    inval : slot=%lx, va=%016lx, psize: %d, local: %d\n",
403 		slot, va, psize, local);
404 	want_v = hpte_encode_v(va, psize, MMU_SEGSIZE_256M);
405 	pss = (psize == MMU_PAGE_4K) ? -1UL : mmu_psize_defs[psize].penc;
406 
407 	lpar_rc = beat_invalidate_htab_entry3(0, slot, want_v, pss);
408 
409 	/* E_busy can be valid output: page may be already replaced */
410 	BUG_ON(lpar_rc != 0 && lpar_rc != 0xfffffff7);
411 }
412 
_beat_lpar_hptab_clear_v3(void)413 static int64_t _beat_lpar_hptab_clear_v3(void)
414 {
415 	return beat_clear_htab3(0);
416 }
417 
beat_lpar_hptab_clear_v3(void)418 static void beat_lpar_hptab_clear_v3(void)
419 {
420 	_beat_lpar_hptab_clear_v3();
421 }
422 
hpte_init_beat_v3(void)423 void __init hpte_init_beat_v3(void)
424 {
425 	if (_beat_lpar_hptab_clear_v3() == 0) {
426 		ppc_md.hpte_invalidate	= beat_lpar_hpte_invalidate_v3;
427 		ppc_md.hpte_updatepp	= beat_lpar_hpte_updatepp_v3;
428 		ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;
429 		ppc_md.hpte_insert	= beat_lpar_hpte_insert_v3;
430 		ppc_md.hpte_remove	= beat_lpar_hpte_remove;
431 		ppc_md.hpte_clear_all	= beat_lpar_hptab_clear_v3;
432 	} else {
433 		ppc_md.hpte_invalidate	= beat_lpar_hpte_invalidate;
434 		ppc_md.hpte_updatepp	= beat_lpar_hpte_updatepp;
435 		ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp;
436 		ppc_md.hpte_insert	= beat_lpar_hpte_insert;
437 		ppc_md.hpte_remove	= beat_lpar_hpte_remove;
438 		ppc_md.hpte_clear_all	= beat_lpar_hptab_clear;
439 	}
440 }
441