tlb-radix.c 11.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * TLB flush routines for radix kernels.
 *
 * Copyright 2015-2016, Aneesh Kumar K.V, IBM Corporation.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/mm.h>
#include <linux/hugetlb.h>
#include <linux/memblock.h>
15
#include <asm/ppc-opcode.h>
16 17 18 19 20

#include <asm/tlb.h>
#include <asm/tlbflush.h>


21 22 23 24 25 26
#define RIC_FLUSH_TLB 0
#define RIC_FLUSH_PWC 1
#define RIC_FLUSH_ALL 2

static inline void __tlbiel_pid(unsigned long pid, int set,
				unsigned long ric)
27
{
28
	unsigned long rb,rs,prs,r;
29 30 31 32 33 34 35

	rb = PPC_BIT(53); /* IS = 1 */
	rb |= set << PPC_BITLSHIFT(51);
	rs = ((unsigned long)pid) << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

36
	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
37 38 39 40 41 42
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
}

/*
 * We use 128 set in radix mode and 256 set in hpt mode.
 */
43
static inline void _tlbiel_pid(unsigned long pid, unsigned long ric)
44 45 46
{
	int set;

47
	asm volatile("ptesync": : :"memory");
48 49 50 51 52 53 54 55 56 57 58 59

	/*
	 * Flush the first set of the TLB, and if we're doing a RIC_FLUSH_ALL,
	 * also flush the entire Page Walk Cache.
	 */
	__tlbiel_pid(pid, 0, ric);

	if (ric == RIC_FLUSH_ALL)
		/* For the remaining sets, just flush the TLB */
		ric = RIC_FLUSH_TLB;

	for (set = 1; set < POWER9_TLB_SETS_RADIX ; set++)
60
		__tlbiel_pid(pid, set, ric);
61

62
	asm volatile("ptesync": : :"memory");
63
	asm volatile(PPC_INVALIDATE_ERAT "; isync" : : :"memory");
64 65
}

66 67 68 69 70 71 72 73 74 75 76
static inline void tlbiel_pwc(unsigned long pid)
{
	asm volatile("ptesync": : :"memory");

	/* For PWC flush, we don't look at set number */
	__tlbiel_pid(pid, 0, RIC_FLUSH_PWC);

	asm volatile("ptesync": : :"memory");
	asm volatile(PPC_INVALIDATE_ERAT "; isync" : : :"memory");
}

77
static inline void _tlbie_pid(unsigned long pid, unsigned long ric)
78
{
79
	unsigned long rb,rs,prs,r;
80 81 82 83 84 85 86

	rb = PPC_BIT(53); /* IS = 1 */
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
87
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
88 89 90 91 92
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
}

static inline void _tlbiel_va(unsigned long va, unsigned long pid,
93
			      unsigned long ap, unsigned long ric)
94
{
95
	unsigned long rb,rs,prs,r;
96 97 98 99 100 101 102 103

	rb = va & ~(PPC_BITMASK(52, 63));
	rb |= ap << PPC_BITLSHIFT(58);
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
104
	asm volatile(PPC_TLBIEL(%0, %4, %3, %2, %1)
105 106 107 108 109
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("ptesync": : :"memory");
}

static inline void _tlbie_va(unsigned long va, unsigned long pid,
110
			     unsigned long ap, unsigned long ric)
111
{
112
	unsigned long rb,rs,prs,r;
113 114 115 116 117 118 119 120

	rb = va & ~(PPC_BITMASK(52, 63));
	rb |= ap << PPC_BITLSHIFT(58);
	rs = pid << PPC_BITLSHIFT(31);
	prs = 1; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
121
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
}

/*
 * Base TLB flushing operations:
 *
 *  - flush_tlb_mm(mm) flushes the specified mm context TLB's
 *  - flush_tlb_page(vma, vmaddr) flushes one page
 *  - flush_tlb_range(vma, start, end) flushes a range of pages
 *  - flush_tlb_kernel_range(start, end) flushes kernel pages
 *
 *  - local_* variants of page and mm only apply to the current
 *    processor
 */
void radix__local_flush_tlb_mm(struct mm_struct *mm)
{
139
	unsigned long pid;
140 141 142 143

	preempt_disable();
	pid = mm->context.id;
	if (pid != MMU_NO_CONTEXT)
144
		_tlbiel_pid(pid, RIC_FLUSH_ALL);
145 146 147 148
	preempt_enable();
}
EXPORT_SYMBOL(radix__local_flush_tlb_mm);

149 150 151 152
void radix__local_flush_tlb_pwc(struct mmu_gather *tlb, unsigned long addr)
{
	unsigned long pid;
	struct mm_struct *mm = tlb->mm;
153 154 155 156 157 158
	/*
	 * If we are doing a full mm flush, we will do a tlb flush
	 * with RIC_FLUSH_ALL later.
	 */
	if (tlb->fullmm)
		return;
159 160 161 162 163

	preempt_disable();

	pid = mm->context.id;
	if (pid != MMU_NO_CONTEXT)
164
		tlbiel_pwc(pid);
165 166 167 168 169

	preempt_enable();
}
EXPORT_SYMBOL(radix__local_flush_tlb_pwc);

170
void radix__local_flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
171
				       int psize)
172
{
173
	unsigned long pid;
174
	unsigned long ap = mmu_get_ap(psize);
175 176 177 178

	preempt_disable();
	pid = mm ? mm->context.id : 0;
	if (pid != MMU_NO_CONTEXT)
179
		_tlbiel_va(vmaddr, pid, ap, RIC_FLUSH_TLB);
180 181 182 183 184
	preempt_enable();
}

void radix__local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
185 186 187 188 189
#ifdef CONFIG_HUGETLB_PAGE
	/* need the return fix for nohash.c */
	if (vma && is_vm_hugetlb_page(vma))
		return __local_flush_hugetlb_page(vma, vmaddr);
#endif
190
	radix__local_flush_tlb_page_psize(vma ? vma->vm_mm : NULL, vmaddr,
191
					  mmu_virtual_psize);
192 193 194 195 196 197
}
EXPORT_SYMBOL(radix__local_flush_tlb_page);

#ifdef CONFIG_SMP
void radix__flush_tlb_mm(struct mm_struct *mm)
{
198
	unsigned long pid;
199 200 201 202 203 204

	preempt_disable();
	pid = mm->context.id;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto no_context;

205
	if (!mm_is_thread_local(mm))
206
		_tlbie_pid(pid, RIC_FLUSH_ALL);
207
	else
208
		_tlbiel_pid(pid, RIC_FLUSH_ALL);
209 210 211 212 213
no_context:
	preempt_enable();
}
EXPORT_SYMBOL(radix__flush_tlb_mm);

214 215 216 217 218
void radix__flush_tlb_pwc(struct mmu_gather *tlb, unsigned long addr)
{
	unsigned long pid;
	struct mm_struct *mm = tlb->mm;

219 220 221 222 223 224
	/*
	 * If we are doing a full mm flush, we will do a tlb flush
	 * with RIC_FLUSH_ALL later.
	 */
	if (tlb->fullmm)
		return;
225 226 227 228 229 230
	preempt_disable();

	pid = mm->context.id;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto no_context;

231
	if (!mm_is_thread_local(mm))
232
		_tlbie_pid(pid, RIC_FLUSH_PWC);
233
	else
234
		tlbiel_pwc(pid);
235 236 237 238 239
no_context:
	preempt_enable();
}
EXPORT_SYMBOL(radix__flush_tlb_pwc);

240
void radix__flush_tlb_page_psize(struct mm_struct *mm, unsigned long vmaddr,
241
				 int psize)
242
{
243
	unsigned long pid;
244
	unsigned long ap = mmu_get_ap(psize);
245 246 247 248 249

	preempt_disable();
	pid = mm ? mm->context.id : 0;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto bail;
250
	if (!mm_is_thread_local(mm))
251
		_tlbie_va(vmaddr, pid, ap, RIC_FLUSH_TLB);
252
	else
253
		_tlbiel_va(vmaddr, pid, ap, RIC_FLUSH_TLB);
254 255 256 257 258 259
bail:
	preempt_enable();
}

void radix__flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr)
{
260 261 262 263
#ifdef CONFIG_HUGETLB_PAGE
	if (vma && is_vm_hugetlb_page(vma))
		return flush_hugetlb_page(vma, vmaddr);
#endif
264
	radix__flush_tlb_page_psize(vma ? vma->vm_mm : NULL, vmaddr,
265
				    mmu_virtual_psize);
266 267 268 269 270 271 272
}
EXPORT_SYMBOL(radix__flush_tlb_page);

#endif /* CONFIG_SMP */

void radix__flush_tlb_kernel_range(unsigned long start, unsigned long end)
{
273
	_tlbie_pid(0, RIC_FLUSH_ALL);
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
}
EXPORT_SYMBOL(radix__flush_tlb_kernel_range);

/*
 * Currently, for range flushing, we just do a full mm flush. Because
 * we use this in code path where we don' track the page size.
 */
void radix__flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
		     unsigned long end)

{
	struct mm_struct *mm = vma->vm_mm;
	radix__flush_tlb_mm(mm);
}
EXPORT_SYMBOL(radix__flush_tlb_range);

290 291 292 293 294 295 296 297 298 299 300 301 302 303
static int radix_get_mmu_psize(int page_size)
{
	int psize;

	if (page_size == (1UL << mmu_psize_defs[mmu_virtual_psize].shift))
		psize = mmu_virtual_psize;
	else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_2M].shift))
		psize = MMU_PAGE_2M;
	else if (page_size == (1UL << mmu_psize_defs[MMU_PAGE_1G].shift))
		psize = MMU_PAGE_1G;
	else
		return -1;
	return psize;
}
304 305 306

void radix__tlb_flush(struct mmu_gather *tlb)
{
307
	int psize = 0;
308
	struct mm_struct *mm = tlb->mm;
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
	int page_size = tlb->page_size;

	psize = radix_get_mmu_psize(page_size);
	/*
	 * if page size is not something we understand, do a full mm flush
	 */
	if (psize != -1 && !tlb->fullmm && !tlb->need_flush_all)
		radix__flush_tlb_range_psize(mm, tlb->start, tlb->end, psize);
	else
		radix__flush_tlb_mm(mm);
}

#define TLB_FLUSH_ALL -1UL
/*
 * Number of pages above which we will do a bcast tlbie. Just a
 * number at this point copied from x86
 */
static unsigned long tlb_single_page_flush_ceiling __read_mostly = 33;

void radix__flush_tlb_range_psize(struct mm_struct *mm, unsigned long start,
				  unsigned long end, int psize)
{
	unsigned long pid;
	unsigned long addr;
333
	int local = mm_is_thread_local(mm);
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
	unsigned long ap = mmu_get_ap(psize);
	unsigned long page_size = 1UL << mmu_psize_defs[psize].shift;


	preempt_disable();
	pid = mm ? mm->context.id : 0;
	if (unlikely(pid == MMU_NO_CONTEXT))
		goto err_out;

	if (end == TLB_FLUSH_ALL ||
	    (end - start) > tlb_single_page_flush_ceiling * page_size) {
		if (local)
			_tlbiel_pid(pid, RIC_FLUSH_TLB);
		else
			_tlbie_pid(pid, RIC_FLUSH_TLB);
		goto err_out;
	}
	for (addr = start; addr < end; addr += page_size) {

		if (local)
			_tlbiel_va(addr, pid, ap, RIC_FLUSH_TLB);
355
		else
356 357 358 359
			_tlbie_va(addr, pid, ap, RIC_FLUSH_TLB);
	}
err_out:
	preempt_enable();
360
}
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398

void radix__flush_tlb_lpid_va(unsigned long lpid, unsigned long gpa,
			      unsigned long page_size)
{
	unsigned long rb,rs,prs,r;
	unsigned long ap;
	unsigned long ric = RIC_FLUSH_TLB;

	ap = mmu_get_ap(radix_get_mmu_psize(page_size));
	rb = gpa & ~(PPC_BITMASK(52, 63));
	rb |= ap << PPC_BITLSHIFT(58);
	rs = lpid & ((1UL << 32) - 1);
	prs = 0; /* process scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
EXPORT_SYMBOL(radix__flush_tlb_lpid_va);

void radix__flush_tlb_lpid(unsigned long lpid)
{
	unsigned long rb,rs,prs,r;
	unsigned long ric = RIC_FLUSH_ALL;

	rb = 0x2 << PPC_BITLSHIFT(53); /* IS = 2 */
	rs = lpid & ((1UL << 32) - 1);
	prs = 0; /* partition scoped */
	r = 1;   /* raidx format */

	asm volatile("ptesync": : :"memory");
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(rs) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
EXPORT_SYMBOL(radix__flush_tlb_lpid);
399 400 401 402 403 404 405

void radix__flush_pmd_tlb_range(struct vm_area_struct *vma,
				unsigned long start, unsigned long end)
{
	radix__flush_tlb_range_psize(vma->vm_mm, start, end, MMU_PAGE_2M);
}
EXPORT_SYMBOL(radix__flush_pmd_tlb_range);
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429

void radix__flush_tlb_all(void)
{
	unsigned long rb,prs,r,rs;
	unsigned long ric = RIC_FLUSH_ALL;

	rb = 0x3 << PPC_BITLSHIFT(53); /* IS = 3 */
	prs = 0; /* partition scoped */
	r = 1;   /* raidx format */
	rs = 1 & ((1UL << 32) - 1); /* any LPID value to flush guest mappings */

	asm volatile("ptesync": : :"memory");
	/*
	 * now flush guest entries by passing PRS = 1 and LPID != 0
	 */
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(1), "i"(ric), "r"(rs) : "memory");
	/*
	 * now flush host entires by passing PRS = 0 and LPID == 0
	 */
	asm volatile(PPC_TLBIE_5(%0, %4, %3, %2, %1)
		     : : "r"(rb), "i"(r), "i"(prs), "i"(ric), "r"(0) : "memory");
	asm volatile("eieio; tlbsync; ptesync": : :"memory");
}
430 431 432 433 434 435 436 437 438 439 440 441 442

void radix__flush_tlb_pte_p9_dd1(unsigned long old_pte, struct mm_struct *mm,
				 unsigned long address)
{
	/*
	 * We track page size in pte only for DD1, So we can
	 * call this only on DD1.
	 */
	if (!cpu_has_feature(CPU_FTR_POWER9_DD1)) {
		VM_WARN_ON(1);
		return;
	}

443
	if (old_pte & R_PAGE_LARGE)
444 445 446 447
		radix__flush_tlb_page_psize(mm, address, MMU_PAGE_2M);
	else
		radix__flush_tlb_page_psize(mm, address, mmu_virtual_psize);
}