xref: /kernel/linux/linux-6.6/arch/mips/mm/tlbex.c (revision 62306a36)
162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
362306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
462306a36Sopenharmony_ci * for more details.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Synthesize TLB refill handlers at runtime.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2004, 2005, 2006, 2008	 Thiemo Seufer
962306a36Sopenharmony_ci * Copyright (C) 2005, 2007, 2008, 2009	 Maciej W. Rozycki
1062306a36Sopenharmony_ci * Copyright (C) 2006  Ralf Baechle (ralf@linux-mips.org)
1162306a36Sopenharmony_ci * Copyright (C) 2008, 2009 Cavium Networks, Inc.
1262306a36Sopenharmony_ci * Copyright (C) 2011  MIPS Technologies, Inc.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * ... and the days got worse and worse and now you see
1562306a36Sopenharmony_ci * I've gone completely out of my mind.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci * They're coming to take me a away haha
1862306a36Sopenharmony_ci * they're coming to take me a away hoho hihi haha
1962306a36Sopenharmony_ci * to the funny farm where code is beautiful all the time ...
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci * (Condolences to Napoleon XIV)
2262306a36Sopenharmony_ci */
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <linux/bug.h>
2562306a36Sopenharmony_ci#include <linux/export.h>
2662306a36Sopenharmony_ci#include <linux/kernel.h>
2762306a36Sopenharmony_ci#include <linux/types.h>
2862306a36Sopenharmony_ci#include <linux/smp.h>
2962306a36Sopenharmony_ci#include <linux/string.h>
3062306a36Sopenharmony_ci#include <linux/cache.h>
3162306a36Sopenharmony_ci#include <linux/pgtable.h>
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <asm/cacheflush.h>
3462306a36Sopenharmony_ci#include <asm/cpu-type.h>
3562306a36Sopenharmony_ci#include <asm/mmu_context.h>
3662306a36Sopenharmony_ci#include <asm/uasm.h>
3762306a36Sopenharmony_ci#include <asm/setup.h>
3862306a36Sopenharmony_ci#include <asm/tlbex.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int mips_xpa_disabled;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int __init xpa_disable(char *s)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	mips_xpa_disabled = 1;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	return 1;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci__setup("noxpa", xpa_disable);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/*
5262306a36Sopenharmony_ci * TLB load/store/modify handlers.
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Only the fastpath gets synthesized at runtime, the slowpath for
5562306a36Sopenharmony_ci * do_page_fault remains normal asm.
5662306a36Sopenharmony_ci */
5762306a36Sopenharmony_ciextern void tlb_do_page_fault_0(void);
5862306a36Sopenharmony_ciextern void tlb_do_page_fault_1(void);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistruct work_registers {
6162306a36Sopenharmony_ci	int r1;
6262306a36Sopenharmony_ci	int r2;
6362306a36Sopenharmony_ci	int r3;
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct tlb_reg_save {
6762306a36Sopenharmony_ci	unsigned long a;
6862306a36Sopenharmony_ci	unsigned long b;
6962306a36Sopenharmony_ci} ____cacheline_aligned_in_smp;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic struct tlb_reg_save handler_reg_save[NR_CPUS];
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic inline int r45k_bvahwbug(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	/* XXX: We should probe for the presence of this bug, but we don't. */
7662306a36Sopenharmony_ci	return 0;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic inline int r4k_250MHZhwbug(void)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	/* XXX: We should probe for the presence of this bug, but we don't. */
8262306a36Sopenharmony_ci	return 0;
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ciextern int sb1250_m3_workaround_needed(void);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline int __maybe_unused bcm1250_m3_war(void)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_SB1_PASS_2_WORKAROUNDS))
9062306a36Sopenharmony_ci		return sb1250_m3_workaround_needed();
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic inline int __maybe_unused r10000_llsc_war(void)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	return IS_ENABLED(CONFIG_WAR_R10000_LLSC);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int use_bbit_insns(void)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	switch (current_cpu_type()) {
10262306a36Sopenharmony_ci	case CPU_CAVIUM_OCTEON:
10362306a36Sopenharmony_ci	case CPU_CAVIUM_OCTEON_PLUS:
10462306a36Sopenharmony_ci	case CPU_CAVIUM_OCTEON2:
10562306a36Sopenharmony_ci	case CPU_CAVIUM_OCTEON3:
10662306a36Sopenharmony_ci		return 1;
10762306a36Sopenharmony_ci	default:
10862306a36Sopenharmony_ci		return 0;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int use_lwx_insns(void)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	switch (current_cpu_type()) {
11562306a36Sopenharmony_ci	case CPU_CAVIUM_OCTEON2:
11662306a36Sopenharmony_ci	case CPU_CAVIUM_OCTEON3:
11762306a36Sopenharmony_ci		return 1;
11862306a36Sopenharmony_ci	default:
11962306a36Sopenharmony_ci		return 0;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci#if defined(CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE) && \
12362306a36Sopenharmony_ci    CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE > 0
12462306a36Sopenharmony_cistatic bool scratchpad_available(void)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	return true;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_cistatic int scratchpad_offset(int i)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	/*
13162306a36Sopenharmony_ci	 * CVMSEG starts at address -32768 and extends for
13262306a36Sopenharmony_ci	 * CAVIUM_OCTEON_CVMSEG_SIZE 128 byte cache lines.
13362306a36Sopenharmony_ci	 */
13462306a36Sopenharmony_ci	i += 1; /* Kernel use starts at the top and works down. */
13562306a36Sopenharmony_ci	return CONFIG_CAVIUM_OCTEON_CVMSEG_SIZE * 128 - (8 * i) - 32768;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci#else
13862306a36Sopenharmony_cistatic bool scratchpad_available(void)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	return false;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_cistatic int scratchpad_offset(int i)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	BUG();
14562306a36Sopenharmony_ci	/* Really unreachable, but evidently some GCC want this. */
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci#endif
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * Found by experiment: At least some revisions of the 4kc throw under
15162306a36Sopenharmony_ci * some circumstances a machine check exception, triggered by invalid
15262306a36Sopenharmony_ci * values in the index register.  Delaying the tlbp instruction until
15362306a36Sopenharmony_ci * after the next branch,  plus adding an additional nop in front of
15462306a36Sopenharmony_ci * tlbwi/tlbwr avoids the invalid index register values. Nobody knows
15562306a36Sopenharmony_ci * why; it's not an issue caused by the core RTL.
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistatic int m4kc_tlbp_war(void)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	return current_cpu_type() == CPU_4KC;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/* Handle labels (which must be positive integers). */
16462306a36Sopenharmony_cienum label_id {
16562306a36Sopenharmony_ci	label_second_part = 1,
16662306a36Sopenharmony_ci	label_leave,
16762306a36Sopenharmony_ci	label_vmalloc,
16862306a36Sopenharmony_ci	label_vmalloc_done,
16962306a36Sopenharmony_ci	label_tlbw_hazard_0,
17062306a36Sopenharmony_ci	label_split = label_tlbw_hazard_0 + 8,
17162306a36Sopenharmony_ci	label_tlbl_goaround1,
17262306a36Sopenharmony_ci	label_tlbl_goaround2,
17362306a36Sopenharmony_ci	label_nopage_tlbl,
17462306a36Sopenharmony_ci	label_nopage_tlbs,
17562306a36Sopenharmony_ci	label_nopage_tlbm,
17662306a36Sopenharmony_ci	label_smp_pgtable_change,
17762306a36Sopenharmony_ci	label_r3000_write_probe_fail,
17862306a36Sopenharmony_ci	label_large_segbits_fault,
17962306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
18062306a36Sopenharmony_ci	label_tlb_huge_update,
18162306a36Sopenharmony_ci#endif
18262306a36Sopenharmony_ci};
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciUASM_L_LA(_second_part)
18562306a36Sopenharmony_ciUASM_L_LA(_leave)
18662306a36Sopenharmony_ciUASM_L_LA(_vmalloc)
18762306a36Sopenharmony_ciUASM_L_LA(_vmalloc_done)
18862306a36Sopenharmony_ci/* _tlbw_hazard_x is handled differently.  */
18962306a36Sopenharmony_ciUASM_L_LA(_split)
19062306a36Sopenharmony_ciUASM_L_LA(_tlbl_goaround1)
19162306a36Sopenharmony_ciUASM_L_LA(_tlbl_goaround2)
19262306a36Sopenharmony_ciUASM_L_LA(_nopage_tlbl)
19362306a36Sopenharmony_ciUASM_L_LA(_nopage_tlbs)
19462306a36Sopenharmony_ciUASM_L_LA(_nopage_tlbm)
19562306a36Sopenharmony_ciUASM_L_LA(_smp_pgtable_change)
19662306a36Sopenharmony_ciUASM_L_LA(_r3000_write_probe_fail)
19762306a36Sopenharmony_ciUASM_L_LA(_large_segbits_fault)
19862306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
19962306a36Sopenharmony_ciUASM_L_LA(_tlb_huge_update)
20062306a36Sopenharmony_ci#endif
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int hazard_instance;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistatic void uasm_bgezl_hazard(u32 **p, struct uasm_reloc **r, int instance)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	switch (instance) {
20762306a36Sopenharmony_ci	case 0 ... 7:
20862306a36Sopenharmony_ci		uasm_il_bgezl(p, r, 0, label_tlbw_hazard_0 + instance);
20962306a36Sopenharmony_ci		return;
21062306a36Sopenharmony_ci	default:
21162306a36Sopenharmony_ci		BUG();
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic void uasm_bgezl_label(struct uasm_label **l, u32 **p, int instance)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	switch (instance) {
21862306a36Sopenharmony_ci	case 0 ... 7:
21962306a36Sopenharmony_ci		uasm_build_label(l, *p, label_tlbw_hazard_0 + instance);
22062306a36Sopenharmony_ci		break;
22162306a36Sopenharmony_ci	default:
22262306a36Sopenharmony_ci		BUG();
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/*
22762306a36Sopenharmony_ci * pgtable bits are assigned dynamically depending on processor feature
22862306a36Sopenharmony_ci * and statically based on kernel configuration.  This spits out the actual
22962306a36Sopenharmony_ci * values the kernel is using.	Required to make sense from disassembled
23062306a36Sopenharmony_ci * TLB exception handlers.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistatic void output_pgtable_bits_defines(void)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci#define pr_define(fmt, ...)					\
23562306a36Sopenharmony_ci	pr_debug("#define " fmt, ##__VA_ARGS__)
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	pr_debug("#include <asm/asm.h>\n");
23862306a36Sopenharmony_ci	pr_debug("#include <asm/regdef.h>\n");
23962306a36Sopenharmony_ci	pr_debug("\n");
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	pr_define("_PAGE_PRESENT_SHIFT %d\n", _PAGE_PRESENT_SHIFT);
24262306a36Sopenharmony_ci	pr_define("_PAGE_NO_READ_SHIFT %d\n", _PAGE_NO_READ_SHIFT);
24362306a36Sopenharmony_ci	pr_define("_PAGE_WRITE_SHIFT %d\n", _PAGE_WRITE_SHIFT);
24462306a36Sopenharmony_ci	pr_define("_PAGE_ACCESSED_SHIFT %d\n", _PAGE_ACCESSED_SHIFT);
24562306a36Sopenharmony_ci	pr_define("_PAGE_MODIFIED_SHIFT %d\n", _PAGE_MODIFIED_SHIFT);
24662306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
24762306a36Sopenharmony_ci	pr_define("_PAGE_HUGE_SHIFT %d\n", _PAGE_HUGE_SHIFT);
24862306a36Sopenharmony_ci#endif
24962306a36Sopenharmony_ci#ifdef _PAGE_NO_EXEC_SHIFT
25062306a36Sopenharmony_ci	if (cpu_has_rixi)
25162306a36Sopenharmony_ci		pr_define("_PAGE_NO_EXEC_SHIFT %d\n", _PAGE_NO_EXEC_SHIFT);
25262306a36Sopenharmony_ci#endif
25362306a36Sopenharmony_ci	pr_define("_PAGE_GLOBAL_SHIFT %d\n", _PAGE_GLOBAL_SHIFT);
25462306a36Sopenharmony_ci	pr_define("_PAGE_VALID_SHIFT %d\n", _PAGE_VALID_SHIFT);
25562306a36Sopenharmony_ci	pr_define("_PAGE_DIRTY_SHIFT %d\n", _PAGE_DIRTY_SHIFT);
25662306a36Sopenharmony_ci	pr_define("PFN_PTE_SHIFT %d\n", PFN_PTE_SHIFT);
25762306a36Sopenharmony_ci	pr_debug("\n");
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic inline void dump_handler(const char *symbol, const void *start, const void *end)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	unsigned int count = (end - start) / sizeof(u32);
26362306a36Sopenharmony_ci	const u32 *handler = start;
26462306a36Sopenharmony_ci	int i;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	pr_debug("LEAF(%s)\n", symbol);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	pr_debug("\t.set push\n");
26962306a36Sopenharmony_ci	pr_debug("\t.set noreorder\n");
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	for (i = 0; i < count; i++)
27262306a36Sopenharmony_ci		pr_debug("\t.word\t0x%08x\t\t# %p\n", handler[i], &handler[i]);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	pr_debug("\t.set\tpop\n");
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	pr_debug("\tEND(%s)\n", symbol);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci/* The only general purpose registers allowed in TLB handlers. */
28062306a36Sopenharmony_ci#define K0		26
28162306a36Sopenharmony_ci#define K1		27
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/* Some CP0 registers */
28462306a36Sopenharmony_ci#define C0_INDEX	0, 0
28562306a36Sopenharmony_ci#define C0_ENTRYLO0	2, 0
28662306a36Sopenharmony_ci#define C0_TCBIND	2, 2
28762306a36Sopenharmony_ci#define C0_ENTRYLO1	3, 0
28862306a36Sopenharmony_ci#define C0_CONTEXT	4, 0
28962306a36Sopenharmony_ci#define C0_PAGEMASK	5, 0
29062306a36Sopenharmony_ci#define C0_PWBASE	5, 5
29162306a36Sopenharmony_ci#define C0_PWFIELD	5, 6
29262306a36Sopenharmony_ci#define C0_PWSIZE	5, 7
29362306a36Sopenharmony_ci#define C0_PWCTL	6, 6
29462306a36Sopenharmony_ci#define C0_BADVADDR	8, 0
29562306a36Sopenharmony_ci#define C0_PGD		9, 7
29662306a36Sopenharmony_ci#define C0_ENTRYHI	10, 0
29762306a36Sopenharmony_ci#define C0_EPC		14, 0
29862306a36Sopenharmony_ci#define C0_XCONTEXT	20, 0
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci#ifdef CONFIG_64BIT
30162306a36Sopenharmony_ci# define GET_CONTEXT(buf, reg) UASM_i_MFC0(buf, reg, C0_XCONTEXT)
30262306a36Sopenharmony_ci#else
30362306a36Sopenharmony_ci# define GET_CONTEXT(buf, reg) UASM_i_MFC0(buf, reg, C0_CONTEXT)
30462306a36Sopenharmony_ci#endif
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/* The worst case length of the handler is around 18 instructions for
30762306a36Sopenharmony_ci * R3000-style TLBs and up to 63 instructions for R4000-style TLBs.
30862306a36Sopenharmony_ci * Maximum space available is 32 instructions for R3000 and 64
30962306a36Sopenharmony_ci * instructions for R4000.
31062306a36Sopenharmony_ci *
31162306a36Sopenharmony_ci * We deliberately chose a buffer size of 128, so we won't scribble
31262306a36Sopenharmony_ci * over anything important on overflow before we panic.
31362306a36Sopenharmony_ci */
31462306a36Sopenharmony_cistatic u32 tlb_handler[128];
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/* simply assume worst case size for labels and relocs */
31762306a36Sopenharmony_cistatic struct uasm_label labels[128];
31862306a36Sopenharmony_cistatic struct uasm_reloc relocs[128];
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cistatic int check_for_high_segbits;
32162306a36Sopenharmony_cistatic bool fill_includes_sw_bits;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic unsigned int kscratch_used_mask;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_cistatic inline int __maybe_unused c0_kscratch(void)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	return 31;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic int allocate_kscratch(void)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	int r;
33362306a36Sopenharmony_ci	unsigned int a = cpu_data[0].kscratch_mask & ~kscratch_used_mask;
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	r = ffs(a);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (r == 0)
33862306a36Sopenharmony_ci		return -1;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	r--; /* make it zero based */
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	kscratch_used_mask |= (1 << r);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return r;
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int scratch_reg;
34862306a36Sopenharmony_ciint pgd_reg;
34962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pgd_reg);
35062306a36Sopenharmony_cienum vmalloc64_mode {not_refill, refill_scratch, refill_noscratch};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic struct work_registers build_get_work_registers(u32 **p)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct work_registers r;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (scratch_reg >= 0) {
35762306a36Sopenharmony_ci		/* Save in CPU local C0_KScratch? */
35862306a36Sopenharmony_ci		UASM_i_MTC0(p, 1, c0_kscratch(), scratch_reg);
35962306a36Sopenharmony_ci		r.r1 = K0;
36062306a36Sopenharmony_ci		r.r2 = K1;
36162306a36Sopenharmony_ci		r.r3 = 1;
36262306a36Sopenharmony_ci		return r;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (num_possible_cpus() > 1) {
36662306a36Sopenharmony_ci		/* Get smp_processor_id */
36762306a36Sopenharmony_ci		UASM_i_CPUID_MFC0(p, K0, SMP_CPUID_REG);
36862306a36Sopenharmony_ci		UASM_i_SRL_SAFE(p, K0, K0, SMP_CPUID_REGSHIFT);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/* handler_reg_save index in K0 */
37162306a36Sopenharmony_ci		UASM_i_SLL(p, K0, K0, ilog2(sizeof(struct tlb_reg_save)));
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		UASM_i_LA(p, K1, (long)&handler_reg_save);
37462306a36Sopenharmony_ci		UASM_i_ADDU(p, K0, K0, K1);
37562306a36Sopenharmony_ci	} else {
37662306a36Sopenharmony_ci		UASM_i_LA(p, K0, (long)&handler_reg_save);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	/* K0 now points to save area, save $1 and $2  */
37962306a36Sopenharmony_ci	UASM_i_SW(p, 1, offsetof(struct tlb_reg_save, a), K0);
38062306a36Sopenharmony_ci	UASM_i_SW(p, 2, offsetof(struct tlb_reg_save, b), K0);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	r.r1 = K1;
38362306a36Sopenharmony_ci	r.r2 = 1;
38462306a36Sopenharmony_ci	r.r3 = 2;
38562306a36Sopenharmony_ci	return r;
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cistatic void build_restore_work_registers(u32 **p)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	if (scratch_reg >= 0) {
39162306a36Sopenharmony_ci		uasm_i_ehb(p);
39262306a36Sopenharmony_ci		UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg);
39362306a36Sopenharmony_ci		return;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci	/* K0 already points to save area, restore $1 and $2  */
39662306a36Sopenharmony_ci	UASM_i_LW(p, 1, offsetof(struct tlb_reg_save, a), K0);
39762306a36Sopenharmony_ci	UASM_i_LW(p, 2, offsetof(struct tlb_reg_save, b), K0);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci/*
40362306a36Sopenharmony_ci * CONFIG_MIPS_PGD_C0_CONTEXT implies 64 bit and lack of pgd_current,
40462306a36Sopenharmony_ci * we cannot do r3000 under these circumstances.
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * The R3000 TLB handler is simple.
40762306a36Sopenharmony_ci */
40862306a36Sopenharmony_cistatic void build_r3000_tlb_refill_handler(void)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	long pgdc = (long)pgd_current;
41162306a36Sopenharmony_ci	u32 *p;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	memset(tlb_handler, 0, sizeof(tlb_handler));
41462306a36Sopenharmony_ci	p = tlb_handler;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	uasm_i_mfc0(&p, K0, C0_BADVADDR);
41762306a36Sopenharmony_ci	uasm_i_lui(&p, K1, uasm_rel_hi(pgdc)); /* cp0 delay */
41862306a36Sopenharmony_ci	uasm_i_lw(&p, K1, uasm_rel_lo(pgdc), K1);
41962306a36Sopenharmony_ci	uasm_i_srl(&p, K0, K0, 22); /* load delay */
42062306a36Sopenharmony_ci	uasm_i_sll(&p, K0, K0, 2);
42162306a36Sopenharmony_ci	uasm_i_addu(&p, K1, K1, K0);
42262306a36Sopenharmony_ci	uasm_i_mfc0(&p, K0, C0_CONTEXT);
42362306a36Sopenharmony_ci	uasm_i_lw(&p, K1, 0, K1); /* cp0 delay */
42462306a36Sopenharmony_ci	uasm_i_andi(&p, K0, K0, 0xffc); /* load delay */
42562306a36Sopenharmony_ci	uasm_i_addu(&p, K1, K1, K0);
42662306a36Sopenharmony_ci	uasm_i_lw(&p, K0, 0, K1);
42762306a36Sopenharmony_ci	uasm_i_nop(&p); /* load delay */
42862306a36Sopenharmony_ci	uasm_i_mtc0(&p, K0, C0_ENTRYLO0);
42962306a36Sopenharmony_ci	uasm_i_mfc0(&p, K1, C0_EPC); /* cp0 delay */
43062306a36Sopenharmony_ci	uasm_i_tlbwr(&p); /* cp0 delay */
43162306a36Sopenharmony_ci	uasm_i_jr(&p, K1);
43262306a36Sopenharmony_ci	uasm_i_rfe(&p); /* branch delay */
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (p > tlb_handler + 32)
43562306a36Sopenharmony_ci		panic("TLB refill handler space exceeded");
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	pr_debug("Wrote TLB refill handler (%u instructions).\n",
43862306a36Sopenharmony_ci		 (unsigned int)(p - tlb_handler));
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	memcpy((void *)ebase, tlb_handler, 0x80);
44162306a36Sopenharmony_ci	local_flush_icache_range(ebase, ebase + 0x80);
44262306a36Sopenharmony_ci	dump_handler("r3000_tlb_refill", (u32 *)ebase, (u32 *)(ebase + 0x80));
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci#endif /* CONFIG_MIPS_PGD_C0_CONTEXT */
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/*
44762306a36Sopenharmony_ci * The R4000 TLB handler is much more complicated. We have two
44862306a36Sopenharmony_ci * consecutive handler areas with 32 instructions space each.
44962306a36Sopenharmony_ci * Since they aren't used at the same time, we can overflow in the
45062306a36Sopenharmony_ci * other one.To keep things simple, we first assume linear space,
45162306a36Sopenharmony_ci * then we relocate it to the final handler layout as needed.
45262306a36Sopenharmony_ci */
45362306a36Sopenharmony_cistatic u32 final_handler[64];
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci/*
45662306a36Sopenharmony_ci * Hazards
45762306a36Sopenharmony_ci *
45862306a36Sopenharmony_ci * From the IDT errata for the QED RM5230 (Nevada), processor revision 1.0:
45962306a36Sopenharmony_ci * 2. A timing hazard exists for the TLBP instruction.
46062306a36Sopenharmony_ci *
46162306a36Sopenharmony_ci *	stalling_instruction
46262306a36Sopenharmony_ci *	TLBP
46362306a36Sopenharmony_ci *
46462306a36Sopenharmony_ci * The JTLB is being read for the TLBP throughout the stall generated by the
46562306a36Sopenharmony_ci * previous instruction. This is not really correct as the stalling instruction
46662306a36Sopenharmony_ci * can modify the address used to access the JTLB.  The failure symptom is that
46762306a36Sopenharmony_ci * the TLBP instruction will use an address created for the stalling instruction
46862306a36Sopenharmony_ci * and not the address held in C0_ENHI and thus report the wrong results.
46962306a36Sopenharmony_ci *
47062306a36Sopenharmony_ci * The software work-around is to not allow the instruction preceding the TLBP
47162306a36Sopenharmony_ci * to stall - make it an NOP or some other instruction guaranteed not to stall.
47262306a36Sopenharmony_ci *
47362306a36Sopenharmony_ci * Errata 2 will not be fixed.	This errata is also on the R5000.
47462306a36Sopenharmony_ci *
47562306a36Sopenharmony_ci * As if we MIPS hackers wouldn't know how to nop pipelines happy ...
47662306a36Sopenharmony_ci */
47762306a36Sopenharmony_cistatic void __maybe_unused build_tlb_probe_entry(u32 **p)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	switch (current_cpu_type()) {
48062306a36Sopenharmony_ci	/* Found by experiment: R4600 v2.0/R4700 needs this, too.  */
48162306a36Sopenharmony_ci	case CPU_R4600:
48262306a36Sopenharmony_ci	case CPU_R4700:
48362306a36Sopenharmony_ci	case CPU_R5000:
48462306a36Sopenharmony_ci	case CPU_NEVADA:
48562306a36Sopenharmony_ci		uasm_i_nop(p);
48662306a36Sopenharmony_ci		uasm_i_tlbp(p);
48762306a36Sopenharmony_ci		break;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	default:
49062306a36Sopenharmony_ci		uasm_i_tlbp(p);
49162306a36Sopenharmony_ci		break;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_civoid build_tlb_write_entry(u32 **p, struct uasm_label **l,
49662306a36Sopenharmony_ci			   struct uasm_reloc **r,
49762306a36Sopenharmony_ci			   enum tlb_write_entry wmode)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	void(*tlbw)(u32 **) = NULL;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	switch (wmode) {
50262306a36Sopenharmony_ci	case tlb_random: tlbw = uasm_i_tlbwr; break;
50362306a36Sopenharmony_ci	case tlb_indexed: tlbw = uasm_i_tlbwi; break;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	if (cpu_has_mips_r2_r6) {
50762306a36Sopenharmony_ci		if (cpu_has_mips_r2_exec_hazard)
50862306a36Sopenharmony_ci			uasm_i_ehb(p);
50962306a36Sopenharmony_ci		tlbw(p);
51062306a36Sopenharmony_ci		return;
51162306a36Sopenharmony_ci	}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	switch (current_cpu_type()) {
51462306a36Sopenharmony_ci	case CPU_R4000PC:
51562306a36Sopenharmony_ci	case CPU_R4000SC:
51662306a36Sopenharmony_ci	case CPU_R4000MC:
51762306a36Sopenharmony_ci	case CPU_R4400PC:
51862306a36Sopenharmony_ci	case CPU_R4400SC:
51962306a36Sopenharmony_ci	case CPU_R4400MC:
52062306a36Sopenharmony_ci		/*
52162306a36Sopenharmony_ci		 * This branch uses up a mtc0 hazard nop slot and saves
52262306a36Sopenharmony_ci		 * two nops after the tlbw instruction.
52362306a36Sopenharmony_ci		 */
52462306a36Sopenharmony_ci		uasm_bgezl_hazard(p, r, hazard_instance);
52562306a36Sopenharmony_ci		tlbw(p);
52662306a36Sopenharmony_ci		uasm_bgezl_label(l, p, hazard_instance);
52762306a36Sopenharmony_ci		hazard_instance++;
52862306a36Sopenharmony_ci		uasm_i_nop(p);
52962306a36Sopenharmony_ci		break;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	case CPU_R4600:
53262306a36Sopenharmony_ci	case CPU_R4700:
53362306a36Sopenharmony_ci		uasm_i_nop(p);
53462306a36Sopenharmony_ci		tlbw(p);
53562306a36Sopenharmony_ci		uasm_i_nop(p);
53662306a36Sopenharmony_ci		break;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	case CPU_R5000:
53962306a36Sopenharmony_ci	case CPU_NEVADA:
54062306a36Sopenharmony_ci		uasm_i_nop(p); /* QED specifies 2 nops hazard */
54162306a36Sopenharmony_ci		uasm_i_nop(p); /* QED specifies 2 nops hazard */
54262306a36Sopenharmony_ci		tlbw(p);
54362306a36Sopenharmony_ci		break;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	case CPU_R4300:
54662306a36Sopenharmony_ci	case CPU_5KC:
54762306a36Sopenharmony_ci	case CPU_TX49XX:
54862306a36Sopenharmony_ci	case CPU_PR4450:
54962306a36Sopenharmony_ci		uasm_i_nop(p);
55062306a36Sopenharmony_ci		tlbw(p);
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	case CPU_R10000:
55462306a36Sopenharmony_ci	case CPU_R12000:
55562306a36Sopenharmony_ci	case CPU_R14000:
55662306a36Sopenharmony_ci	case CPU_R16000:
55762306a36Sopenharmony_ci	case CPU_4KC:
55862306a36Sopenharmony_ci	case CPU_4KEC:
55962306a36Sopenharmony_ci	case CPU_M14KC:
56062306a36Sopenharmony_ci	case CPU_M14KEC:
56162306a36Sopenharmony_ci	case CPU_SB1:
56262306a36Sopenharmony_ci	case CPU_SB1A:
56362306a36Sopenharmony_ci	case CPU_4KSC:
56462306a36Sopenharmony_ci	case CPU_20KC:
56562306a36Sopenharmony_ci	case CPU_25KF:
56662306a36Sopenharmony_ci	case CPU_BMIPS32:
56762306a36Sopenharmony_ci	case CPU_BMIPS3300:
56862306a36Sopenharmony_ci	case CPU_BMIPS4350:
56962306a36Sopenharmony_ci	case CPU_BMIPS4380:
57062306a36Sopenharmony_ci	case CPU_BMIPS5000:
57162306a36Sopenharmony_ci	case CPU_LOONGSON2EF:
57262306a36Sopenharmony_ci	case CPU_LOONGSON64:
57362306a36Sopenharmony_ci	case CPU_R5500:
57462306a36Sopenharmony_ci		if (m4kc_tlbp_war())
57562306a36Sopenharmony_ci			uasm_i_nop(p);
57662306a36Sopenharmony_ci		fallthrough;
57762306a36Sopenharmony_ci	case CPU_ALCHEMY:
57862306a36Sopenharmony_ci		tlbw(p);
57962306a36Sopenharmony_ci		break;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	case CPU_RM7000:
58262306a36Sopenharmony_ci		uasm_i_nop(p);
58362306a36Sopenharmony_ci		uasm_i_nop(p);
58462306a36Sopenharmony_ci		uasm_i_nop(p);
58562306a36Sopenharmony_ci		uasm_i_nop(p);
58662306a36Sopenharmony_ci		tlbw(p);
58762306a36Sopenharmony_ci		break;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	case CPU_XBURST:
59062306a36Sopenharmony_ci		tlbw(p);
59162306a36Sopenharmony_ci		uasm_i_nop(p);
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	default:
59562306a36Sopenharmony_ci		panic("No TLB refill handler yet (CPU type: %d)",
59662306a36Sopenharmony_ci		      current_cpu_type());
59762306a36Sopenharmony_ci		break;
59862306a36Sopenharmony_ci	}
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(build_tlb_write_entry);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic __maybe_unused void build_convert_pte_to_entrylo(u32 **p,
60362306a36Sopenharmony_ci							unsigned int reg)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	if (_PAGE_GLOBAL_SHIFT == 0) {
60662306a36Sopenharmony_ci		/* pte_t is already in EntryLo format */
60762306a36Sopenharmony_ci		return;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	if (cpu_has_rixi && _PAGE_NO_EXEC != 0) {
61162306a36Sopenharmony_ci		if (fill_includes_sw_bits) {
61262306a36Sopenharmony_ci			UASM_i_ROTR(p, reg, reg, ilog2(_PAGE_GLOBAL));
61362306a36Sopenharmony_ci		} else {
61462306a36Sopenharmony_ci			UASM_i_SRL(p, reg, reg, ilog2(_PAGE_NO_EXEC));
61562306a36Sopenharmony_ci			UASM_i_ROTR(p, reg, reg,
61662306a36Sopenharmony_ci				    ilog2(_PAGE_GLOBAL) - ilog2(_PAGE_NO_EXEC));
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci	} else {
61962306a36Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT
62062306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, reg, reg, ilog2(_PAGE_GLOBAL));
62162306a36Sopenharmony_ci#else
62262306a36Sopenharmony_ci		UASM_i_SRL(p, reg, reg, ilog2(_PAGE_GLOBAL));
62362306a36Sopenharmony_ci#endif
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_cistatic void build_restore_pagemask(u32 **p, struct uasm_reloc **r,
63062306a36Sopenharmony_ci				   unsigned int tmp, enum label_id lid,
63162306a36Sopenharmony_ci				   int restore_scratch)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	if (restore_scratch) {
63462306a36Sopenharmony_ci		/*
63562306a36Sopenharmony_ci		 * Ensure the MFC0 below observes the value written to the
63662306a36Sopenharmony_ci		 * KScratch register by the prior MTC0.
63762306a36Sopenharmony_ci		 */
63862306a36Sopenharmony_ci		if (scratch_reg >= 0)
63962306a36Sopenharmony_ci			uasm_i_ehb(p);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		/* Reset default page size */
64262306a36Sopenharmony_ci		if (PM_DEFAULT_MASK >> 16) {
64362306a36Sopenharmony_ci			uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16);
64462306a36Sopenharmony_ci			uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff);
64562306a36Sopenharmony_ci			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
64662306a36Sopenharmony_ci			uasm_il_b(p, r, lid);
64762306a36Sopenharmony_ci		} else if (PM_DEFAULT_MASK) {
64862306a36Sopenharmony_ci			uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK);
64962306a36Sopenharmony_ci			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
65062306a36Sopenharmony_ci			uasm_il_b(p, r, lid);
65162306a36Sopenharmony_ci		} else {
65262306a36Sopenharmony_ci			uasm_i_mtc0(p, 0, C0_PAGEMASK);
65362306a36Sopenharmony_ci			uasm_il_b(p, r, lid);
65462306a36Sopenharmony_ci		}
65562306a36Sopenharmony_ci		if (scratch_reg >= 0)
65662306a36Sopenharmony_ci			UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg);
65762306a36Sopenharmony_ci		else
65862306a36Sopenharmony_ci			UASM_i_LW(p, 1, scratchpad_offset(0), 0);
65962306a36Sopenharmony_ci	} else {
66062306a36Sopenharmony_ci		/* Reset default page size */
66162306a36Sopenharmony_ci		if (PM_DEFAULT_MASK >> 16) {
66262306a36Sopenharmony_ci			uasm_i_lui(p, tmp, PM_DEFAULT_MASK >> 16);
66362306a36Sopenharmony_ci			uasm_i_ori(p, tmp, tmp, PM_DEFAULT_MASK & 0xffff);
66462306a36Sopenharmony_ci			uasm_il_b(p, r, lid);
66562306a36Sopenharmony_ci			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
66662306a36Sopenharmony_ci		} else if (PM_DEFAULT_MASK) {
66762306a36Sopenharmony_ci			uasm_i_ori(p, tmp, 0, PM_DEFAULT_MASK);
66862306a36Sopenharmony_ci			uasm_il_b(p, r, lid);
66962306a36Sopenharmony_ci			uasm_i_mtc0(p, tmp, C0_PAGEMASK);
67062306a36Sopenharmony_ci		} else {
67162306a36Sopenharmony_ci			uasm_il_b(p, r, lid);
67262306a36Sopenharmony_ci			uasm_i_mtc0(p, 0, C0_PAGEMASK);
67362306a36Sopenharmony_ci		}
67462306a36Sopenharmony_ci	}
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_cistatic void build_huge_tlb_write_entry(u32 **p, struct uasm_label **l,
67862306a36Sopenharmony_ci				       struct uasm_reloc **r,
67962306a36Sopenharmony_ci				       unsigned int tmp,
68062306a36Sopenharmony_ci				       enum tlb_write_entry wmode,
68162306a36Sopenharmony_ci				       int restore_scratch)
68262306a36Sopenharmony_ci{
68362306a36Sopenharmony_ci	/* Set huge page tlb entry size */
68462306a36Sopenharmony_ci	uasm_i_lui(p, tmp, PM_HUGE_MASK >> 16);
68562306a36Sopenharmony_ci	uasm_i_ori(p, tmp, tmp, PM_HUGE_MASK & 0xffff);
68662306a36Sopenharmony_ci	uasm_i_mtc0(p, tmp, C0_PAGEMASK);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	build_tlb_write_entry(p, l, r, wmode);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	build_restore_pagemask(p, r, tmp, label_leave, restore_scratch);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci/*
69462306a36Sopenharmony_ci * Check if Huge PTE is present, if so then jump to LABEL.
69562306a36Sopenharmony_ci */
69662306a36Sopenharmony_cistatic void
69762306a36Sopenharmony_cibuild_is_huge_pte(u32 **p, struct uasm_reloc **r, unsigned int tmp,
69862306a36Sopenharmony_ci		  unsigned int pmd, int lid)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	UASM_i_LW(p, tmp, 0, pmd);
70162306a36Sopenharmony_ci	if (use_bbit_insns()) {
70262306a36Sopenharmony_ci		uasm_il_bbit1(p, r, tmp, ilog2(_PAGE_HUGE), lid);
70362306a36Sopenharmony_ci	} else {
70462306a36Sopenharmony_ci		uasm_i_andi(p, tmp, tmp, _PAGE_HUGE);
70562306a36Sopenharmony_ci		uasm_il_bnez(p, r, tmp, lid);
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic void build_huge_update_entries(u32 **p, unsigned int pte,
71062306a36Sopenharmony_ci				      unsigned int tmp)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	int small_sequence;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	/*
71562306a36Sopenharmony_ci	 * A huge PTE describes an area the size of the
71662306a36Sopenharmony_ci	 * configured huge page size. This is twice the
71762306a36Sopenharmony_ci	 * of the large TLB entry size we intend to use.
71862306a36Sopenharmony_ci	 * A TLB entry half the size of the configured
71962306a36Sopenharmony_ci	 * huge page size is configured into entrylo0
72062306a36Sopenharmony_ci	 * and entrylo1 to cover the contiguous huge PTE
72162306a36Sopenharmony_ci	 * address space.
72262306a36Sopenharmony_ci	 */
72362306a36Sopenharmony_ci	small_sequence = (HPAGE_SIZE >> 7) < 0x10000;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	/* We can clobber tmp.	It isn't used after this.*/
72662306a36Sopenharmony_ci	if (!small_sequence)
72762306a36Sopenharmony_ci		uasm_i_lui(p, tmp, HPAGE_SIZE >> (7 + 16));
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	build_convert_pte_to_entrylo(p, pte);
73062306a36Sopenharmony_ci	UASM_i_MTC0(p, pte, C0_ENTRYLO0); /* load it */
73162306a36Sopenharmony_ci	/* convert to entrylo1 */
73262306a36Sopenharmony_ci	if (small_sequence)
73362306a36Sopenharmony_ci		UASM_i_ADDIU(p, pte, pte, HPAGE_SIZE >> 7);
73462306a36Sopenharmony_ci	else
73562306a36Sopenharmony_ci		UASM_i_ADDU(p, pte, pte, tmp);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	UASM_i_MTC0(p, pte, C0_ENTRYLO1); /* load it */
73862306a36Sopenharmony_ci}
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic void build_huge_handler_tail(u32 **p, struct uasm_reloc **r,
74162306a36Sopenharmony_ci				    struct uasm_label **l,
74262306a36Sopenharmony_ci				    unsigned int pte,
74362306a36Sopenharmony_ci				    unsigned int ptr,
74462306a36Sopenharmony_ci				    unsigned int flush)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci#ifdef CONFIG_SMP
74762306a36Sopenharmony_ci	UASM_i_SC(p, pte, 0, ptr);
74862306a36Sopenharmony_ci	uasm_il_beqz(p, r, pte, label_tlb_huge_update);
74962306a36Sopenharmony_ci	UASM_i_LW(p, pte, 0, ptr); /* Needed because SC killed our PTE */
75062306a36Sopenharmony_ci#else
75162306a36Sopenharmony_ci	UASM_i_SW(p, pte, 0, ptr);
75262306a36Sopenharmony_ci#endif
75362306a36Sopenharmony_ci	if (cpu_has_ftlb && flush) {
75462306a36Sopenharmony_ci		BUG_ON(!cpu_has_tlbinv);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		UASM_i_MFC0(p, ptr, C0_ENTRYHI);
75762306a36Sopenharmony_ci		uasm_i_ori(p, ptr, ptr, MIPS_ENTRYHI_EHINV);
75862306a36Sopenharmony_ci		UASM_i_MTC0(p, ptr, C0_ENTRYHI);
75962306a36Sopenharmony_ci		build_tlb_write_entry(p, l, r, tlb_indexed);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci		uasm_i_xori(p, ptr, ptr, MIPS_ENTRYHI_EHINV);
76262306a36Sopenharmony_ci		UASM_i_MTC0(p, ptr, C0_ENTRYHI);
76362306a36Sopenharmony_ci		build_huge_update_entries(p, pte, ptr);
76462306a36Sopenharmony_ci		build_huge_tlb_write_entry(p, l, r, pte, tlb_random, 0);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		return;
76762306a36Sopenharmony_ci	}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	build_huge_update_entries(p, pte, ptr);
77062306a36Sopenharmony_ci	build_huge_tlb_write_entry(p, l, r, pte, tlb_indexed, 0);
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci#endif /* CONFIG_MIPS_HUGE_TLB_SUPPORT */
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci#ifdef CONFIG_64BIT
77562306a36Sopenharmony_ci/*
77662306a36Sopenharmony_ci * TMP and PTR are scratch.
77762306a36Sopenharmony_ci * TMP will be clobbered, PTR will hold the pmd entry.
77862306a36Sopenharmony_ci */
77962306a36Sopenharmony_civoid build_get_pmde64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
78062306a36Sopenharmony_ci		      unsigned int tmp, unsigned int ptr)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT
78362306a36Sopenharmony_ci	long pgdc = (long)pgd_current;
78462306a36Sopenharmony_ci#endif
78562306a36Sopenharmony_ci	/*
78662306a36Sopenharmony_ci	 * The vmalloc handling is not in the hotpath.
78762306a36Sopenharmony_ci	 */
78862306a36Sopenharmony_ci	uasm_i_dmfc0(p, tmp, C0_BADVADDR);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (check_for_high_segbits) {
79162306a36Sopenharmony_ci		/*
79262306a36Sopenharmony_ci		 * The kernel currently implicitely assumes that the
79362306a36Sopenharmony_ci		 * MIPS SEGBITS parameter for the processor is
79462306a36Sopenharmony_ci		 * (PGDIR_SHIFT+PGDIR_BITS) or less, and will never
79562306a36Sopenharmony_ci		 * allocate virtual addresses outside the maximum
79662306a36Sopenharmony_ci		 * range for SEGBITS = (PGDIR_SHIFT+PGDIR_BITS). But
79762306a36Sopenharmony_ci		 * that doesn't prevent user code from accessing the
79862306a36Sopenharmony_ci		 * higher xuseg addresses.  Here, we make sure that
79962306a36Sopenharmony_ci		 * everything but the lower xuseg addresses goes down
80062306a36Sopenharmony_ci		 * the module_alloc/vmalloc path.
80162306a36Sopenharmony_ci		 */
80262306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, ptr, tmp, PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
80362306a36Sopenharmony_ci		uasm_il_bnez(p, r, ptr, label_vmalloc);
80462306a36Sopenharmony_ci	} else {
80562306a36Sopenharmony_ci		uasm_il_bltz(p, r, tmp, label_vmalloc);
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci	/* No uasm_i_nop needed here, since the next insn doesn't touch TMP. */
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	if (pgd_reg != -1) {
81062306a36Sopenharmony_ci		/* pgd is in pgd_reg */
81162306a36Sopenharmony_ci		if (cpu_has_ldpte)
81262306a36Sopenharmony_ci			UASM_i_MFC0(p, ptr, C0_PWBASE);
81362306a36Sopenharmony_ci		else
81462306a36Sopenharmony_ci			UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg);
81562306a36Sopenharmony_ci	} else {
81662306a36Sopenharmony_ci#if defined(CONFIG_MIPS_PGD_C0_CONTEXT)
81762306a36Sopenharmony_ci		/*
81862306a36Sopenharmony_ci		 * &pgd << 11 stored in CONTEXT [23..63].
81962306a36Sopenharmony_ci		 */
82062306a36Sopenharmony_ci		UASM_i_MFC0(p, ptr, C0_CONTEXT);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci		/* Clear lower 23 bits of context. */
82362306a36Sopenharmony_ci		uasm_i_dins(p, ptr, 0, 0, 23);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci		/* insert bit[63:59] of CAC_BASE into bit[11:6] of ptr */
82662306a36Sopenharmony_ci		uasm_i_ori(p, ptr, ptr, ((u64)(CAC_BASE) >> 53));
82762306a36Sopenharmony_ci		uasm_i_drotr(p, ptr, ptr, 11);
82862306a36Sopenharmony_ci#elif defined(CONFIG_SMP)
82962306a36Sopenharmony_ci		UASM_i_CPUID_MFC0(p, ptr, SMP_CPUID_REG);
83062306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, ptr, ptr, SMP_CPUID_PTRSHIFT);
83162306a36Sopenharmony_ci		UASM_i_LA_mostly(p, tmp, pgdc);
83262306a36Sopenharmony_ci		uasm_i_daddu(p, ptr, ptr, tmp);
83362306a36Sopenharmony_ci		uasm_i_dmfc0(p, tmp, C0_BADVADDR);
83462306a36Sopenharmony_ci		uasm_i_ld(p, ptr, uasm_rel_lo(pgdc), ptr);
83562306a36Sopenharmony_ci#else
83662306a36Sopenharmony_ci		UASM_i_LA_mostly(p, ptr, pgdc);
83762306a36Sopenharmony_ci		uasm_i_ld(p, ptr, uasm_rel_lo(pgdc), ptr);
83862306a36Sopenharmony_ci#endif
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	uasm_l_vmalloc_done(l, *p);
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	/* get pgd offset in bytes */
84462306a36Sopenharmony_ci	uasm_i_dsrl_safe(p, tmp, tmp, PGDIR_SHIFT - 3);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	uasm_i_andi(p, tmp, tmp, (PTRS_PER_PGD - 1)<<3);
84762306a36Sopenharmony_ci	uasm_i_daddu(p, ptr, ptr, tmp); /* add in pgd offset */
84862306a36Sopenharmony_ci#ifndef __PAGETABLE_PUD_FOLDED
84962306a36Sopenharmony_ci	uasm_i_dmfc0(p, tmp, C0_BADVADDR); /* get faulting address */
85062306a36Sopenharmony_ci	uasm_i_ld(p, ptr, 0, ptr); /* get pud pointer */
85162306a36Sopenharmony_ci	uasm_i_dsrl_safe(p, tmp, tmp, PUD_SHIFT - 3); /* get pud offset in bytes */
85262306a36Sopenharmony_ci	uasm_i_andi(p, tmp, tmp, (PTRS_PER_PUD - 1) << 3);
85362306a36Sopenharmony_ci	uasm_i_daddu(p, ptr, ptr, tmp); /* add in pud offset */
85462306a36Sopenharmony_ci#endif
85562306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
85662306a36Sopenharmony_ci	uasm_i_dmfc0(p, tmp, C0_BADVADDR); /* get faulting address */
85762306a36Sopenharmony_ci	uasm_i_ld(p, ptr, 0, ptr); /* get pmd pointer */
85862306a36Sopenharmony_ci	uasm_i_dsrl_safe(p, tmp, tmp, PMD_SHIFT-3); /* get pmd offset in bytes */
85962306a36Sopenharmony_ci	uasm_i_andi(p, tmp, tmp, (PTRS_PER_PMD - 1)<<3);
86062306a36Sopenharmony_ci	uasm_i_daddu(p, ptr, ptr, tmp); /* add in pmd offset */
86162306a36Sopenharmony_ci#endif
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(build_get_pmde64);
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci/*
86662306a36Sopenharmony_ci * BVADDR is the faulting address, PTR is scratch.
86762306a36Sopenharmony_ci * PTR will hold the pgd for vmalloc.
86862306a36Sopenharmony_ci */
86962306a36Sopenharmony_cistatic void
87062306a36Sopenharmony_cibuild_get_pgd_vmalloc64(u32 **p, struct uasm_label **l, struct uasm_reloc **r,
87162306a36Sopenharmony_ci			unsigned int bvaddr, unsigned int ptr,
87262306a36Sopenharmony_ci			enum vmalloc64_mode mode)
87362306a36Sopenharmony_ci{
87462306a36Sopenharmony_ci	long swpd = (long)swapper_pg_dir;
87562306a36Sopenharmony_ci	int single_insn_swpd;
87662306a36Sopenharmony_ci	int did_vmalloc_branch = 0;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	single_insn_swpd = uasm_in_compat_space_p(swpd) && !uasm_rel_lo(swpd);
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	uasm_l_vmalloc(l, *p);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (mode != not_refill && check_for_high_segbits) {
88362306a36Sopenharmony_ci		if (single_insn_swpd) {
88462306a36Sopenharmony_ci			uasm_il_bltz(p, r, bvaddr, label_vmalloc_done);
88562306a36Sopenharmony_ci			uasm_i_lui(p, ptr, uasm_rel_hi(swpd));
88662306a36Sopenharmony_ci			did_vmalloc_branch = 1;
88762306a36Sopenharmony_ci			/* fall through */
88862306a36Sopenharmony_ci		} else {
88962306a36Sopenharmony_ci			uasm_il_bgez(p, r, bvaddr, label_large_segbits_fault);
89062306a36Sopenharmony_ci		}
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci	if (!did_vmalloc_branch) {
89362306a36Sopenharmony_ci		if (single_insn_swpd) {
89462306a36Sopenharmony_ci			uasm_il_b(p, r, label_vmalloc_done);
89562306a36Sopenharmony_ci			uasm_i_lui(p, ptr, uasm_rel_hi(swpd));
89662306a36Sopenharmony_ci		} else {
89762306a36Sopenharmony_ci			UASM_i_LA_mostly(p, ptr, swpd);
89862306a36Sopenharmony_ci			uasm_il_b(p, r, label_vmalloc_done);
89962306a36Sopenharmony_ci			if (uasm_in_compat_space_p(swpd))
90062306a36Sopenharmony_ci				uasm_i_addiu(p, ptr, ptr, uasm_rel_lo(swpd));
90162306a36Sopenharmony_ci			else
90262306a36Sopenharmony_ci				uasm_i_daddiu(p, ptr, ptr, uasm_rel_lo(swpd));
90362306a36Sopenharmony_ci		}
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci	if (mode != not_refill && check_for_high_segbits) {
90662306a36Sopenharmony_ci		uasm_l_large_segbits_fault(l, *p);
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci		if (mode == refill_scratch && scratch_reg >= 0)
90962306a36Sopenharmony_ci			uasm_i_ehb(p);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci		/*
91262306a36Sopenharmony_ci		 * We get here if we are an xsseg address, or if we are
91362306a36Sopenharmony_ci		 * an xuseg address above (PGDIR_SHIFT+PGDIR_BITS) boundary.
91462306a36Sopenharmony_ci		 *
91562306a36Sopenharmony_ci		 * Ignoring xsseg (assume disabled so would generate
91662306a36Sopenharmony_ci		 * (address errors?), the only remaining possibility
91762306a36Sopenharmony_ci		 * is the upper xuseg addresses.  On processors with
91862306a36Sopenharmony_ci		 * TLB_SEGBITS <= PGDIR_SHIFT+PGDIR_BITS, these
91962306a36Sopenharmony_ci		 * addresses would have taken an address error. We try
92062306a36Sopenharmony_ci		 * to mimic that here by taking a load/istream page
92162306a36Sopenharmony_ci		 * fault.
92262306a36Sopenharmony_ci		 */
92362306a36Sopenharmony_ci		if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
92462306a36Sopenharmony_ci			uasm_i_sync(p, 0);
92562306a36Sopenharmony_ci		UASM_i_LA(p, ptr, (unsigned long)tlb_do_page_fault_0);
92662306a36Sopenharmony_ci		uasm_i_jr(p, ptr);
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci		if (mode == refill_scratch) {
92962306a36Sopenharmony_ci			if (scratch_reg >= 0)
93062306a36Sopenharmony_ci				UASM_i_MFC0(p, 1, c0_kscratch(), scratch_reg);
93162306a36Sopenharmony_ci			else
93262306a36Sopenharmony_ci				UASM_i_LW(p, 1, scratchpad_offset(0), 0);
93362306a36Sopenharmony_ci		} else {
93462306a36Sopenharmony_ci			uasm_i_nop(p);
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci	}
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci#else /* !CONFIG_64BIT */
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci/*
94262306a36Sopenharmony_ci * TMP and PTR are scratch.
94362306a36Sopenharmony_ci * TMP will be clobbered, PTR will hold the pgd entry.
94462306a36Sopenharmony_ci */
94562306a36Sopenharmony_civoid build_get_pgde32(u32 **p, unsigned int tmp, unsigned int ptr)
94662306a36Sopenharmony_ci{
94762306a36Sopenharmony_ci	if (pgd_reg != -1) {
94862306a36Sopenharmony_ci		/* pgd is in pgd_reg */
94962306a36Sopenharmony_ci		uasm_i_mfc0(p, ptr, c0_kscratch(), pgd_reg);
95062306a36Sopenharmony_ci		uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
95162306a36Sopenharmony_ci	} else {
95262306a36Sopenharmony_ci		long pgdc = (long)pgd_current;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		/* 32 bit SMP has smp_processor_id() stored in CONTEXT. */
95562306a36Sopenharmony_ci#ifdef CONFIG_SMP
95662306a36Sopenharmony_ci		uasm_i_mfc0(p, ptr, SMP_CPUID_REG);
95762306a36Sopenharmony_ci		UASM_i_LA_mostly(p, tmp, pgdc);
95862306a36Sopenharmony_ci		uasm_i_srl(p, ptr, ptr, SMP_CPUID_PTRSHIFT);
95962306a36Sopenharmony_ci		uasm_i_addu(p, ptr, tmp, ptr);
96062306a36Sopenharmony_ci#else
96162306a36Sopenharmony_ci		UASM_i_LA_mostly(p, ptr, pgdc);
96262306a36Sopenharmony_ci#endif
96362306a36Sopenharmony_ci		uasm_i_mfc0(p, tmp, C0_BADVADDR); /* get faulting address */
96462306a36Sopenharmony_ci		uasm_i_lw(p, ptr, uasm_rel_lo(pgdc), ptr);
96562306a36Sopenharmony_ci	}
96662306a36Sopenharmony_ci	uasm_i_srl(p, tmp, tmp, PGDIR_SHIFT); /* get pgd only bits */
96762306a36Sopenharmony_ci	uasm_i_sll(p, tmp, tmp, PGD_T_LOG2);
96862306a36Sopenharmony_ci	uasm_i_addu(p, ptr, ptr, tmp); /* add in pgd offset */
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(build_get_pgde32);
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci#endif /* !CONFIG_64BIT */
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic void build_adjust_context(u32 **p, unsigned int ctx)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	unsigned int shift = 4 - (PTE_T_LOG2 + 1) + PAGE_SHIFT - 12;
97762306a36Sopenharmony_ci	unsigned int mask = (PTRS_PER_PTE / 2 - 1) << (PTE_T_LOG2 + 1);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	if (shift)
98062306a36Sopenharmony_ci		UASM_i_SRL(p, ctx, ctx, shift);
98162306a36Sopenharmony_ci	uasm_i_andi(p, ctx, ctx, mask);
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_civoid build_get_ptep(u32 **p, unsigned int tmp, unsigned int ptr)
98562306a36Sopenharmony_ci{
98662306a36Sopenharmony_ci	/*
98762306a36Sopenharmony_ci	 * Bug workaround for the Nevada. It seems as if under certain
98862306a36Sopenharmony_ci	 * circumstances the move from cp0_context might produce a
98962306a36Sopenharmony_ci	 * bogus result when the mfc0 instruction and its consumer are
99062306a36Sopenharmony_ci	 * in a different cacheline or a load instruction, probably any
99162306a36Sopenharmony_ci	 * memory reference, is between them.
99262306a36Sopenharmony_ci	 */
99362306a36Sopenharmony_ci	switch (current_cpu_type()) {
99462306a36Sopenharmony_ci	case CPU_NEVADA:
99562306a36Sopenharmony_ci		UASM_i_LW(p, ptr, 0, ptr);
99662306a36Sopenharmony_ci		GET_CONTEXT(p, tmp); /* get context reg */
99762306a36Sopenharmony_ci		break;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	default:
100062306a36Sopenharmony_ci		GET_CONTEXT(p, tmp); /* get context reg */
100162306a36Sopenharmony_ci		UASM_i_LW(p, ptr, 0, ptr);
100262306a36Sopenharmony_ci		break;
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	build_adjust_context(p, tmp);
100662306a36Sopenharmony_ci	UASM_i_ADDU(p, ptr, ptr, tmp); /* add in offset */
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(build_get_ptep);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_civoid build_update_entries(u32 **p, unsigned int tmp, unsigned int ptep)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	int pte_off_even = 0;
101362306a36Sopenharmony_ci	int pte_off_odd = sizeof(pte_t);
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci#if defined(CONFIG_CPU_MIPS32) && defined(CONFIG_PHYS_ADDR_T_64BIT)
101662306a36Sopenharmony_ci	/* The low 32 bits of EntryLo is stored in pte_high */
101762306a36Sopenharmony_ci	pte_off_even += offsetof(pte_t, pte_high);
101862306a36Sopenharmony_ci	pte_off_odd += offsetof(pte_t, pte_high);
101962306a36Sopenharmony_ci#endif
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_XPA)) {
102262306a36Sopenharmony_ci		uasm_i_lw(p, tmp, pte_off_even, ptep); /* even pte */
102362306a36Sopenharmony_ci		UASM_i_ROTR(p, tmp, tmp, ilog2(_PAGE_GLOBAL));
102462306a36Sopenharmony_ci		UASM_i_MTC0(p, tmp, C0_ENTRYLO0);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		if (cpu_has_xpa && !mips_xpa_disabled) {
102762306a36Sopenharmony_ci			uasm_i_lw(p, tmp, 0, ptep);
102862306a36Sopenharmony_ci			uasm_i_ext(p, tmp, tmp, 0, 24);
102962306a36Sopenharmony_ci			uasm_i_mthc0(p, tmp, C0_ENTRYLO0);
103062306a36Sopenharmony_ci		}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci		uasm_i_lw(p, tmp, pte_off_odd, ptep); /* odd pte */
103362306a36Sopenharmony_ci		UASM_i_ROTR(p, tmp, tmp, ilog2(_PAGE_GLOBAL));
103462306a36Sopenharmony_ci		UASM_i_MTC0(p, tmp, C0_ENTRYLO1);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci		if (cpu_has_xpa && !mips_xpa_disabled) {
103762306a36Sopenharmony_ci			uasm_i_lw(p, tmp, sizeof(pte_t), ptep);
103862306a36Sopenharmony_ci			uasm_i_ext(p, tmp, tmp, 0, 24);
103962306a36Sopenharmony_ci			uasm_i_mthc0(p, tmp, C0_ENTRYLO1);
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci		return;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	UASM_i_LW(p, tmp, pte_off_even, ptep); /* get even pte */
104562306a36Sopenharmony_ci	UASM_i_LW(p, ptep, pte_off_odd, ptep); /* get odd pte */
104662306a36Sopenharmony_ci	if (r45k_bvahwbug())
104762306a36Sopenharmony_ci		build_tlb_probe_entry(p);
104862306a36Sopenharmony_ci	build_convert_pte_to_entrylo(p, tmp);
104962306a36Sopenharmony_ci	if (r4k_250MHZhwbug())
105062306a36Sopenharmony_ci		UASM_i_MTC0(p, 0, C0_ENTRYLO0);
105162306a36Sopenharmony_ci	UASM_i_MTC0(p, tmp, C0_ENTRYLO0); /* load it */
105262306a36Sopenharmony_ci	build_convert_pte_to_entrylo(p, ptep);
105362306a36Sopenharmony_ci	if (r45k_bvahwbug())
105462306a36Sopenharmony_ci		uasm_i_mfc0(p, tmp, C0_INDEX);
105562306a36Sopenharmony_ci	if (r4k_250MHZhwbug())
105662306a36Sopenharmony_ci		UASM_i_MTC0(p, 0, C0_ENTRYLO1);
105762306a36Sopenharmony_ci	UASM_i_MTC0(p, ptep, C0_ENTRYLO1); /* load it */
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(build_update_entries);
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistruct mips_huge_tlb_info {
106262306a36Sopenharmony_ci	int huge_pte;
106362306a36Sopenharmony_ci	int restore_scratch;
106462306a36Sopenharmony_ci	bool need_reload_pte;
106562306a36Sopenharmony_ci};
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic struct mips_huge_tlb_info
106862306a36Sopenharmony_cibuild_fast_tlb_refill_handler (u32 **p, struct uasm_label **l,
106962306a36Sopenharmony_ci			       struct uasm_reloc **r, unsigned int tmp,
107062306a36Sopenharmony_ci			       unsigned int ptr, int c0_scratch_reg)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	struct mips_huge_tlb_info rv;
107362306a36Sopenharmony_ci	unsigned int even, odd;
107462306a36Sopenharmony_ci	int vmalloc_branch_delay_filled = 0;
107562306a36Sopenharmony_ci	const int scratch = 1; /* Our extra working register */
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	rv.huge_pte = scratch;
107862306a36Sopenharmony_ci	rv.restore_scratch = 0;
107962306a36Sopenharmony_ci	rv.need_reload_pte = false;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	if (check_for_high_segbits) {
108262306a36Sopenharmony_ci		UASM_i_MFC0(p, tmp, C0_BADVADDR);
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_ci		if (pgd_reg != -1)
108562306a36Sopenharmony_ci			UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg);
108662306a36Sopenharmony_ci		else
108762306a36Sopenharmony_ci			UASM_i_MFC0(p, ptr, C0_CONTEXT);
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci		if (c0_scratch_reg >= 0)
109062306a36Sopenharmony_ci			UASM_i_MTC0(p, scratch, c0_kscratch(), c0_scratch_reg);
109162306a36Sopenharmony_ci		else
109262306a36Sopenharmony_ci			UASM_i_SW(p, scratch, scratchpad_offset(0), 0);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, scratch, tmp,
109562306a36Sopenharmony_ci				 PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
109662306a36Sopenharmony_ci		uasm_il_bnez(p, r, scratch, label_vmalloc);
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci		if (pgd_reg == -1) {
109962306a36Sopenharmony_ci			vmalloc_branch_delay_filled = 1;
110062306a36Sopenharmony_ci			/* Clear lower 23 bits of context. */
110162306a36Sopenharmony_ci			uasm_i_dins(p, ptr, 0, 0, 23);
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci	} else {
110462306a36Sopenharmony_ci		if (pgd_reg != -1)
110562306a36Sopenharmony_ci			UASM_i_MFC0(p, ptr, c0_kscratch(), pgd_reg);
110662306a36Sopenharmony_ci		else
110762306a36Sopenharmony_ci			UASM_i_MFC0(p, ptr, C0_CONTEXT);
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci		UASM_i_MFC0(p, tmp, C0_BADVADDR);
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci		if (c0_scratch_reg >= 0)
111262306a36Sopenharmony_ci			UASM_i_MTC0(p, scratch, c0_kscratch(), c0_scratch_reg);
111362306a36Sopenharmony_ci		else
111462306a36Sopenharmony_ci			UASM_i_SW(p, scratch, scratchpad_offset(0), 0);
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci		if (pgd_reg == -1)
111762306a36Sopenharmony_ci			/* Clear lower 23 bits of context. */
111862306a36Sopenharmony_ci			uasm_i_dins(p, ptr, 0, 0, 23);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci		uasm_il_bltz(p, r, tmp, label_vmalloc);
112162306a36Sopenharmony_ci	}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	if (pgd_reg == -1) {
112462306a36Sopenharmony_ci		vmalloc_branch_delay_filled = 1;
112562306a36Sopenharmony_ci		/* insert bit[63:59] of CAC_BASE into bit[11:6] of ptr */
112662306a36Sopenharmony_ci		uasm_i_ori(p, ptr, ptr, ((u64)(CAC_BASE) >> 53));
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci		uasm_i_drotr(p, ptr, ptr, 11);
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci#ifdef __PAGETABLE_PMD_FOLDED
113262306a36Sopenharmony_ci#define LOC_PTEP scratch
113362306a36Sopenharmony_ci#else
113462306a36Sopenharmony_ci#define LOC_PTEP ptr
113562306a36Sopenharmony_ci#endif
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (!vmalloc_branch_delay_filled)
113862306a36Sopenharmony_ci		/* get pgd offset in bytes */
113962306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, scratch, tmp, PGDIR_SHIFT - 3);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	uasm_l_vmalloc_done(l, *p);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_ci	/*
114462306a36Sopenharmony_ci	 *			   tmp		ptr
114562306a36Sopenharmony_ci	 * fall-through case =	 badvaddr  *pgd_current
114662306a36Sopenharmony_ci	 * vmalloc case	     =	 badvaddr  swapper_pg_dir
114762306a36Sopenharmony_ci	 */
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	if (vmalloc_branch_delay_filled)
115062306a36Sopenharmony_ci		/* get pgd offset in bytes */
115162306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, scratch, tmp, PGDIR_SHIFT - 3);
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci#ifdef __PAGETABLE_PMD_FOLDED
115462306a36Sopenharmony_ci	GET_CONTEXT(p, tmp); /* get context reg */
115562306a36Sopenharmony_ci#endif
115662306a36Sopenharmony_ci	uasm_i_andi(p, scratch, scratch, (PTRS_PER_PGD - 1) << 3);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (use_lwx_insns()) {
115962306a36Sopenharmony_ci		UASM_i_LWX(p, LOC_PTEP, scratch, ptr);
116062306a36Sopenharmony_ci	} else {
116162306a36Sopenharmony_ci		uasm_i_daddu(p, ptr, ptr, scratch); /* add in pgd offset */
116262306a36Sopenharmony_ci		uasm_i_ld(p, LOC_PTEP, 0, ptr); /* get pmd pointer */
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci#ifndef __PAGETABLE_PUD_FOLDED
116662306a36Sopenharmony_ci	/* get pud offset in bytes */
116762306a36Sopenharmony_ci	uasm_i_dsrl_safe(p, scratch, tmp, PUD_SHIFT - 3);
116862306a36Sopenharmony_ci	uasm_i_andi(p, scratch, scratch, (PTRS_PER_PUD - 1) << 3);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	if (use_lwx_insns()) {
117162306a36Sopenharmony_ci		UASM_i_LWX(p, ptr, scratch, ptr);
117262306a36Sopenharmony_ci	} else {
117362306a36Sopenharmony_ci		uasm_i_daddu(p, ptr, ptr, scratch); /* add in pmd offset */
117462306a36Sopenharmony_ci		UASM_i_LW(p, ptr, 0, ptr);
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci	/* ptr contains a pointer to PMD entry */
117762306a36Sopenharmony_ci	/* tmp contains the address */
117862306a36Sopenharmony_ci#endif
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
118162306a36Sopenharmony_ci	/* get pmd offset in bytes */
118262306a36Sopenharmony_ci	uasm_i_dsrl_safe(p, scratch, tmp, PMD_SHIFT - 3);
118362306a36Sopenharmony_ci	uasm_i_andi(p, scratch, scratch, (PTRS_PER_PMD - 1) << 3);
118462306a36Sopenharmony_ci	GET_CONTEXT(p, tmp); /* get context reg */
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_ci	if (use_lwx_insns()) {
118762306a36Sopenharmony_ci		UASM_i_LWX(p, scratch, scratch, ptr);
118862306a36Sopenharmony_ci	} else {
118962306a36Sopenharmony_ci		uasm_i_daddu(p, ptr, ptr, scratch); /* add in pmd offset */
119062306a36Sopenharmony_ci		UASM_i_LW(p, scratch, 0, ptr);
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci#endif
119362306a36Sopenharmony_ci	/* Adjust the context during the load latency. */
119462306a36Sopenharmony_ci	build_adjust_context(p, tmp);
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
119762306a36Sopenharmony_ci	uasm_il_bbit1(p, r, scratch, ilog2(_PAGE_HUGE), label_tlb_huge_update);
119862306a36Sopenharmony_ci	/*
119962306a36Sopenharmony_ci	 * The in the LWX case we don't want to do the load in the
120062306a36Sopenharmony_ci	 * delay slot.	It cannot issue in the same cycle and may be
120162306a36Sopenharmony_ci	 * speculative and unneeded.
120262306a36Sopenharmony_ci	 */
120362306a36Sopenharmony_ci	if (use_lwx_insns())
120462306a36Sopenharmony_ci		uasm_i_nop(p);
120562306a36Sopenharmony_ci#endif /* CONFIG_MIPS_HUGE_TLB_SUPPORT */
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	/* build_update_entries */
120962306a36Sopenharmony_ci	if (use_lwx_insns()) {
121062306a36Sopenharmony_ci		even = ptr;
121162306a36Sopenharmony_ci		odd = tmp;
121262306a36Sopenharmony_ci		UASM_i_LWX(p, even, scratch, tmp);
121362306a36Sopenharmony_ci		UASM_i_ADDIU(p, tmp, tmp, sizeof(pte_t));
121462306a36Sopenharmony_ci		UASM_i_LWX(p, odd, scratch, tmp);
121562306a36Sopenharmony_ci	} else {
121662306a36Sopenharmony_ci		UASM_i_ADDU(p, ptr, scratch, tmp); /* add in offset */
121762306a36Sopenharmony_ci		even = tmp;
121862306a36Sopenharmony_ci		odd = ptr;
121962306a36Sopenharmony_ci		UASM_i_LW(p, even, 0, ptr); /* get even pte */
122062306a36Sopenharmony_ci		UASM_i_LW(p, odd, sizeof(pte_t), ptr); /* get odd pte */
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci	if (cpu_has_rixi) {
122362306a36Sopenharmony_ci		uasm_i_drotr(p, even, even, ilog2(_PAGE_GLOBAL));
122462306a36Sopenharmony_ci		UASM_i_MTC0(p, even, C0_ENTRYLO0); /* load it */
122562306a36Sopenharmony_ci		uasm_i_drotr(p, odd, odd, ilog2(_PAGE_GLOBAL));
122662306a36Sopenharmony_ci	} else {
122762306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, even, even, ilog2(_PAGE_GLOBAL));
122862306a36Sopenharmony_ci		UASM_i_MTC0(p, even, C0_ENTRYLO0); /* load it */
122962306a36Sopenharmony_ci		uasm_i_dsrl_safe(p, odd, odd, ilog2(_PAGE_GLOBAL));
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci	UASM_i_MTC0(p, odd, C0_ENTRYLO1); /* load it */
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	if (c0_scratch_reg >= 0) {
123462306a36Sopenharmony_ci		uasm_i_ehb(p);
123562306a36Sopenharmony_ci		UASM_i_MFC0(p, scratch, c0_kscratch(), c0_scratch_reg);
123662306a36Sopenharmony_ci		build_tlb_write_entry(p, l, r, tlb_random);
123762306a36Sopenharmony_ci		uasm_l_leave(l, *p);
123862306a36Sopenharmony_ci		rv.restore_scratch = 1;
123962306a36Sopenharmony_ci	} else if (PAGE_SHIFT == 14 || PAGE_SHIFT == 13)  {
124062306a36Sopenharmony_ci		build_tlb_write_entry(p, l, r, tlb_random);
124162306a36Sopenharmony_ci		uasm_l_leave(l, *p);
124262306a36Sopenharmony_ci		UASM_i_LW(p, scratch, scratchpad_offset(0), 0);
124362306a36Sopenharmony_ci	} else {
124462306a36Sopenharmony_ci		UASM_i_LW(p, scratch, scratchpad_offset(0), 0);
124562306a36Sopenharmony_ci		build_tlb_write_entry(p, l, r, tlb_random);
124662306a36Sopenharmony_ci		uasm_l_leave(l, *p);
124762306a36Sopenharmony_ci		rv.restore_scratch = 1;
124862306a36Sopenharmony_ci	}
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	uasm_i_eret(p); /* return from trap */
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	return rv;
125362306a36Sopenharmony_ci}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci/*
125662306a36Sopenharmony_ci * For a 64-bit kernel, we are using the 64-bit XTLB refill exception
125762306a36Sopenharmony_ci * because EXL == 0.  If we wrap, we can also use the 32 instruction
125862306a36Sopenharmony_ci * slots before the XTLB refill exception handler which belong to the
125962306a36Sopenharmony_ci * unused TLB refill exception.
126062306a36Sopenharmony_ci */
126162306a36Sopenharmony_ci#define MIPS64_REFILL_INSNS 32
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_cistatic void build_r4000_tlb_refill_handler(void)
126462306a36Sopenharmony_ci{
126562306a36Sopenharmony_ci	u32 *p = tlb_handler;
126662306a36Sopenharmony_ci	struct uasm_label *l = labels;
126762306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
126862306a36Sopenharmony_ci	u32 *f;
126962306a36Sopenharmony_ci	unsigned int final_len;
127062306a36Sopenharmony_ci	struct mips_huge_tlb_info htlb_info __maybe_unused;
127162306a36Sopenharmony_ci	enum vmalloc64_mode vmalloc_mode __maybe_unused;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	memset(tlb_handler, 0, sizeof(tlb_handler));
127462306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
127562306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
127662306a36Sopenharmony_ci	memset(final_handler, 0, sizeof(final_handler));
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_64BIT) && (scratch_reg >= 0 || scratchpad_available()) && use_bbit_insns()) {
127962306a36Sopenharmony_ci		htlb_info = build_fast_tlb_refill_handler(&p, &l, &r, K0, K1,
128062306a36Sopenharmony_ci							  scratch_reg);
128162306a36Sopenharmony_ci		vmalloc_mode = refill_scratch;
128262306a36Sopenharmony_ci	} else {
128362306a36Sopenharmony_ci		htlb_info.huge_pte = K0;
128462306a36Sopenharmony_ci		htlb_info.restore_scratch = 0;
128562306a36Sopenharmony_ci		htlb_info.need_reload_pte = true;
128662306a36Sopenharmony_ci		vmalloc_mode = refill_noscratch;
128762306a36Sopenharmony_ci		/*
128862306a36Sopenharmony_ci		 * create the plain linear handler
128962306a36Sopenharmony_ci		 */
129062306a36Sopenharmony_ci		if (bcm1250_m3_war()) {
129162306a36Sopenharmony_ci			unsigned int segbits = 44;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci			uasm_i_dmfc0(&p, K0, C0_BADVADDR);
129462306a36Sopenharmony_ci			uasm_i_dmfc0(&p, K1, C0_ENTRYHI);
129562306a36Sopenharmony_ci			uasm_i_xor(&p, K0, K0, K1);
129662306a36Sopenharmony_ci			uasm_i_dsrl_safe(&p, K1, K0, 62);
129762306a36Sopenharmony_ci			uasm_i_dsrl_safe(&p, K0, K0, 12 + 1);
129862306a36Sopenharmony_ci			uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits);
129962306a36Sopenharmony_ci			uasm_i_or(&p, K0, K0, K1);
130062306a36Sopenharmony_ci			uasm_il_bnez(&p, &r, K0, label_leave);
130162306a36Sopenharmony_ci			/* No need for uasm_i_nop */
130262306a36Sopenharmony_ci		}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci#ifdef CONFIG_64BIT
130562306a36Sopenharmony_ci		build_get_pmde64(&p, &l, &r, K0, K1); /* get pmd in K1 */
130662306a36Sopenharmony_ci#else
130762306a36Sopenharmony_ci		build_get_pgde32(&p, K0, K1); /* get pgd in K1 */
130862306a36Sopenharmony_ci#endif
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
131162306a36Sopenharmony_ci		build_is_huge_pte(&p, &r, K0, K1, label_tlb_huge_update);
131262306a36Sopenharmony_ci#endif
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci		build_get_ptep(&p, K0, K1);
131562306a36Sopenharmony_ci		build_update_entries(&p, K0, K1);
131662306a36Sopenharmony_ci		build_tlb_write_entry(&p, &l, &r, tlb_random);
131762306a36Sopenharmony_ci		uasm_l_leave(&l, p);
131862306a36Sopenharmony_ci		uasm_i_eret(&p); /* return from trap */
131962306a36Sopenharmony_ci	}
132062306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
132162306a36Sopenharmony_ci	uasm_l_tlb_huge_update(&l, p);
132262306a36Sopenharmony_ci	if (htlb_info.need_reload_pte)
132362306a36Sopenharmony_ci		UASM_i_LW(&p, htlb_info.huge_pte, 0, K1);
132462306a36Sopenharmony_ci	build_huge_update_entries(&p, htlb_info.huge_pte, K1);
132562306a36Sopenharmony_ci	build_huge_tlb_write_entry(&p, &l, &r, K0, tlb_random,
132662306a36Sopenharmony_ci				   htlb_info.restore_scratch);
132762306a36Sopenharmony_ci#endif
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci#ifdef CONFIG_64BIT
133062306a36Sopenharmony_ci	build_get_pgd_vmalloc64(&p, &l, &r, K0, K1, vmalloc_mode);
133162306a36Sopenharmony_ci#endif
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	/*
133462306a36Sopenharmony_ci	 * Overflow check: For the 64bit handler, we need at least one
133562306a36Sopenharmony_ci	 * free instruction slot for the wrap-around branch. In worst
133662306a36Sopenharmony_ci	 * case, if the intended insertion point is a delay slot, we
133762306a36Sopenharmony_ci	 * need three, with the second nop'ed and the third being
133862306a36Sopenharmony_ci	 * unused.
133962306a36Sopenharmony_ci	 */
134062306a36Sopenharmony_ci	switch (boot_cpu_type()) {
134162306a36Sopenharmony_ci	default:
134262306a36Sopenharmony_ci		if (sizeof(long) == 4) {
134362306a36Sopenharmony_ci		fallthrough;
134462306a36Sopenharmony_ci	case CPU_LOONGSON2EF:
134562306a36Sopenharmony_ci		/* Loongson2 ebase is different than r4k, we have more space */
134662306a36Sopenharmony_ci			if ((p - tlb_handler) > 64)
134762306a36Sopenharmony_ci				panic("TLB refill handler space exceeded");
134862306a36Sopenharmony_ci			/*
134962306a36Sopenharmony_ci			 * Now fold the handler in the TLB refill handler space.
135062306a36Sopenharmony_ci			 */
135162306a36Sopenharmony_ci			f = final_handler;
135262306a36Sopenharmony_ci			/* Simplest case, just copy the handler. */
135362306a36Sopenharmony_ci			uasm_copy_handler(relocs, labels, tlb_handler, p, f);
135462306a36Sopenharmony_ci			final_len = p - tlb_handler;
135562306a36Sopenharmony_ci			break;
135662306a36Sopenharmony_ci		} else {
135762306a36Sopenharmony_ci			if (((p - tlb_handler) > (MIPS64_REFILL_INSNS * 2) - 1)
135862306a36Sopenharmony_ci			    || (((p - tlb_handler) > (MIPS64_REFILL_INSNS * 2) - 3)
135962306a36Sopenharmony_ci				&& uasm_insn_has_bdelay(relocs,
136062306a36Sopenharmony_ci							tlb_handler + MIPS64_REFILL_INSNS - 3)))
136162306a36Sopenharmony_ci				panic("TLB refill handler space exceeded");
136262306a36Sopenharmony_ci			/*
136362306a36Sopenharmony_ci			 * Now fold the handler in the TLB refill handler space.
136462306a36Sopenharmony_ci			 */
136562306a36Sopenharmony_ci			f = final_handler + MIPS64_REFILL_INSNS;
136662306a36Sopenharmony_ci			if ((p - tlb_handler) <= MIPS64_REFILL_INSNS) {
136762306a36Sopenharmony_ci				/* Just copy the handler. */
136862306a36Sopenharmony_ci				uasm_copy_handler(relocs, labels, tlb_handler, p, f);
136962306a36Sopenharmony_ci				final_len = p - tlb_handler;
137062306a36Sopenharmony_ci			} else {
137162306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
137262306a36Sopenharmony_ci				const enum label_id ls = label_tlb_huge_update;
137362306a36Sopenharmony_ci#else
137462306a36Sopenharmony_ci				const enum label_id ls = label_vmalloc;
137562306a36Sopenharmony_ci#endif
137662306a36Sopenharmony_ci				u32 *split;
137762306a36Sopenharmony_ci				int ov = 0;
137862306a36Sopenharmony_ci				int i;
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_ci				for (i = 0; i < ARRAY_SIZE(labels) && labels[i].lab != ls; i++)
138162306a36Sopenharmony_ci					;
138262306a36Sopenharmony_ci				BUG_ON(i == ARRAY_SIZE(labels));
138362306a36Sopenharmony_ci				split = labels[i].addr;
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_ci				/*
138662306a36Sopenharmony_ci				 * See if we have overflown one way or the other.
138762306a36Sopenharmony_ci				 */
138862306a36Sopenharmony_ci				if (split > tlb_handler + MIPS64_REFILL_INSNS ||
138962306a36Sopenharmony_ci				    split < p - MIPS64_REFILL_INSNS)
139062306a36Sopenharmony_ci					ov = 1;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci				if (ov) {
139362306a36Sopenharmony_ci					/*
139462306a36Sopenharmony_ci					 * Split two instructions before the end.  One
139562306a36Sopenharmony_ci					 * for the branch and one for the instruction
139662306a36Sopenharmony_ci					 * in the delay slot.
139762306a36Sopenharmony_ci					 */
139862306a36Sopenharmony_ci					split = tlb_handler + MIPS64_REFILL_INSNS - 2;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci					/*
140162306a36Sopenharmony_ci					 * If the branch would fall in a delay slot,
140262306a36Sopenharmony_ci					 * we must back up an additional instruction
140362306a36Sopenharmony_ci					 * so that it is no longer in a delay slot.
140462306a36Sopenharmony_ci					 */
140562306a36Sopenharmony_ci					if (uasm_insn_has_bdelay(relocs, split - 1))
140662306a36Sopenharmony_ci						split--;
140762306a36Sopenharmony_ci				}
140862306a36Sopenharmony_ci				/* Copy first part of the handler. */
140962306a36Sopenharmony_ci				uasm_copy_handler(relocs, labels, tlb_handler, split, f);
141062306a36Sopenharmony_ci				f += split - tlb_handler;
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci				if (ov) {
141362306a36Sopenharmony_ci					/* Insert branch. */
141462306a36Sopenharmony_ci					uasm_l_split(&l, final_handler);
141562306a36Sopenharmony_ci					uasm_il_b(&f, &r, label_split);
141662306a36Sopenharmony_ci					if (uasm_insn_has_bdelay(relocs, split))
141762306a36Sopenharmony_ci						uasm_i_nop(&f);
141862306a36Sopenharmony_ci					else {
141962306a36Sopenharmony_ci						uasm_copy_handler(relocs, labels,
142062306a36Sopenharmony_ci								  split, split + 1, f);
142162306a36Sopenharmony_ci						uasm_move_labels(labels, f, f + 1, -1);
142262306a36Sopenharmony_ci						f++;
142362306a36Sopenharmony_ci						split++;
142462306a36Sopenharmony_ci					}
142562306a36Sopenharmony_ci				}
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci				/* Copy the rest of the handler. */
142862306a36Sopenharmony_ci				uasm_copy_handler(relocs, labels, split, p, final_handler);
142962306a36Sopenharmony_ci				final_len = (f - (final_handler + MIPS64_REFILL_INSNS)) +
143062306a36Sopenharmony_ci					    (p - split);
143162306a36Sopenharmony_ci			}
143262306a36Sopenharmony_ci		}
143362306a36Sopenharmony_ci		break;
143462306a36Sopenharmony_ci	}
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
143762306a36Sopenharmony_ci	pr_debug("Wrote TLB refill handler (%u instructions).\n",
143862306a36Sopenharmony_ci		 final_len);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	memcpy((void *)ebase, final_handler, 0x100);
144162306a36Sopenharmony_ci	local_flush_icache_range(ebase, ebase + 0x100);
144262306a36Sopenharmony_ci	dump_handler("r4000_tlb_refill", (u32 *)ebase, (u32 *)(ebase + 0x100));
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_cistatic void setup_pw(void)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	unsigned int pwctl;
144862306a36Sopenharmony_ci	unsigned long pgd_i, pgd_w;
144962306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
145062306a36Sopenharmony_ci	unsigned long pmd_i, pmd_w;
145162306a36Sopenharmony_ci#endif
145262306a36Sopenharmony_ci	unsigned long pt_i, pt_w;
145362306a36Sopenharmony_ci	unsigned long pte_i, pte_w;
145462306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
145562306a36Sopenharmony_ci	unsigned long psn;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	psn = ilog2(_PAGE_HUGE);     /* bit used to indicate huge page */
145862306a36Sopenharmony_ci#endif
145962306a36Sopenharmony_ci	pgd_i = PGDIR_SHIFT;  /* 1st level PGD */
146062306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
146162306a36Sopenharmony_ci	pgd_w = PGDIR_SHIFT - PMD_SHIFT + PGD_TABLE_ORDER;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	pmd_i = PMD_SHIFT;    /* 2nd level PMD */
146462306a36Sopenharmony_ci	pmd_w = PMD_SHIFT - PAGE_SHIFT;
146562306a36Sopenharmony_ci#else
146662306a36Sopenharmony_ci	pgd_w = PGDIR_SHIFT - PAGE_SHIFT + PGD_TABLE_ORDER;
146762306a36Sopenharmony_ci#endif
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci	pt_i  = PAGE_SHIFT;    /* 3rd level PTE */
147062306a36Sopenharmony_ci	pt_w  = PAGE_SHIFT - 3;
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ci	pte_i = ilog2(_PAGE_GLOBAL);
147362306a36Sopenharmony_ci	pte_w = 0;
147462306a36Sopenharmony_ci	pwctl = 1 << 30; /* Set PWDirExt */
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
147762306a36Sopenharmony_ci	write_c0_pwfield(pgd_i << 24 | pmd_i << 12 | pt_i << 6 | pte_i);
147862306a36Sopenharmony_ci	write_c0_pwsize(1 << 30 | pgd_w << 24 | pmd_w << 12 | pt_w << 6 | pte_w);
147962306a36Sopenharmony_ci#else
148062306a36Sopenharmony_ci	write_c0_pwfield(pgd_i << 24 | pt_i << 6 | pte_i);
148162306a36Sopenharmony_ci	write_c0_pwsize(1 << 30 | pgd_w << 24 | pt_w << 6 | pte_w);
148262306a36Sopenharmony_ci#endif
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
148562306a36Sopenharmony_ci	pwctl |= (1 << 6 | psn);
148662306a36Sopenharmony_ci#endif
148762306a36Sopenharmony_ci	write_c0_pwctl(pwctl);
148862306a36Sopenharmony_ci	write_c0_kpgd((long)swapper_pg_dir);
148962306a36Sopenharmony_ci	kscratch_used_mask |= (1 << 7); /* KScratch6 is used for KPGD */
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic void build_loongson3_tlb_refill_handler(void)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	u32 *p = tlb_handler;
149562306a36Sopenharmony_ci	struct uasm_label *l = labels;
149662306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
149962306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
150062306a36Sopenharmony_ci	memset(tlb_handler, 0, sizeof(tlb_handler));
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_ci	if (check_for_high_segbits) {
150362306a36Sopenharmony_ci		uasm_i_dmfc0(&p, K0, C0_BADVADDR);
150462306a36Sopenharmony_ci		uasm_i_dsrl_safe(&p, K1, K0, PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
150562306a36Sopenharmony_ci		uasm_il_beqz(&p, &r, K1, label_vmalloc);
150662306a36Sopenharmony_ci		uasm_i_nop(&p);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci		uasm_il_bgez(&p, &r, K0, label_large_segbits_fault);
150962306a36Sopenharmony_ci		uasm_i_nop(&p);
151062306a36Sopenharmony_ci		uasm_l_vmalloc(&l, p);
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	uasm_i_dmfc0(&p, K1, C0_PGD);
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_ci	uasm_i_lddir(&p, K0, K1, 3);  /* global page dir */
151662306a36Sopenharmony_ci#ifndef __PAGETABLE_PMD_FOLDED
151762306a36Sopenharmony_ci	uasm_i_lddir(&p, K1, K0, 1);  /* middle page dir */
151862306a36Sopenharmony_ci#endif
151962306a36Sopenharmony_ci	uasm_i_ldpte(&p, K1, 0);      /* even */
152062306a36Sopenharmony_ci	uasm_i_ldpte(&p, K1, 1);      /* odd */
152162306a36Sopenharmony_ci	uasm_i_tlbwr(&p);
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	/* restore page mask */
152462306a36Sopenharmony_ci	if (PM_DEFAULT_MASK >> 16) {
152562306a36Sopenharmony_ci		uasm_i_lui(&p, K0, PM_DEFAULT_MASK >> 16);
152662306a36Sopenharmony_ci		uasm_i_ori(&p, K0, K0, PM_DEFAULT_MASK & 0xffff);
152762306a36Sopenharmony_ci		uasm_i_mtc0(&p, K0, C0_PAGEMASK);
152862306a36Sopenharmony_ci	} else if (PM_DEFAULT_MASK) {
152962306a36Sopenharmony_ci		uasm_i_ori(&p, K0, 0, PM_DEFAULT_MASK);
153062306a36Sopenharmony_ci		uasm_i_mtc0(&p, K0, C0_PAGEMASK);
153162306a36Sopenharmony_ci	} else {
153262306a36Sopenharmony_ci		uasm_i_mtc0(&p, 0, C0_PAGEMASK);
153362306a36Sopenharmony_ci	}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	uasm_i_eret(&p);
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	if (check_for_high_segbits) {
153862306a36Sopenharmony_ci		uasm_l_large_segbits_fault(&l, p);
153962306a36Sopenharmony_ci		UASM_i_LA(&p, K1, (unsigned long)tlb_do_page_fault_0);
154062306a36Sopenharmony_ci		uasm_i_jr(&p, K1);
154162306a36Sopenharmony_ci		uasm_i_nop(&p);
154262306a36Sopenharmony_ci	}
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
154562306a36Sopenharmony_ci	memcpy((void *)(ebase + 0x80), tlb_handler, 0x80);
154662306a36Sopenharmony_ci	local_flush_icache_range(ebase + 0x80, ebase + 0x100);
154762306a36Sopenharmony_ci	dump_handler("loongson3_tlb_refill",
154862306a36Sopenharmony_ci		     (u32 *)(ebase + 0x80), (u32 *)(ebase + 0x100));
154962306a36Sopenharmony_ci}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_cistatic void build_setup_pgd(void)
155262306a36Sopenharmony_ci{
155362306a36Sopenharmony_ci	const int a0 = 4;
155462306a36Sopenharmony_ci	const int __maybe_unused a1 = 5;
155562306a36Sopenharmony_ci	const int __maybe_unused a2 = 6;
155662306a36Sopenharmony_ci	u32 *p = (u32 *)msk_isa16_mode((ulong)tlbmiss_handler_setup_pgd);
155762306a36Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT
155862306a36Sopenharmony_ci	long pgdc = (long)pgd_current;
155962306a36Sopenharmony_ci#endif
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_ci	memset(p, 0, tlbmiss_handler_setup_pgd_end - (char *)p);
156262306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
156362306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
156462306a36Sopenharmony_ci	pgd_reg = allocate_kscratch();
156562306a36Sopenharmony_ci#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
156662306a36Sopenharmony_ci	if (pgd_reg == -1) {
156762306a36Sopenharmony_ci		struct uasm_label *l = labels;
156862306a36Sopenharmony_ci		struct uasm_reloc *r = relocs;
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci		/* PGD << 11 in c0_Context */
157162306a36Sopenharmony_ci		/*
157262306a36Sopenharmony_ci		 * If it is a ckseg0 address, convert to a physical
157362306a36Sopenharmony_ci		 * address.  Shifting right by 29 and adding 4 will
157462306a36Sopenharmony_ci		 * result in zero for these addresses.
157562306a36Sopenharmony_ci		 *
157662306a36Sopenharmony_ci		 */
157762306a36Sopenharmony_ci		UASM_i_SRA(&p, a1, a0, 29);
157862306a36Sopenharmony_ci		UASM_i_ADDIU(&p, a1, a1, 4);
157962306a36Sopenharmony_ci		uasm_il_bnez(&p, &r, a1, label_tlbl_goaround1);
158062306a36Sopenharmony_ci		uasm_i_nop(&p);
158162306a36Sopenharmony_ci		uasm_i_dinsm(&p, a0, 0, 29, 64 - 29);
158262306a36Sopenharmony_ci		uasm_l_tlbl_goaround1(&l, p);
158362306a36Sopenharmony_ci		UASM_i_SLL(&p, a0, a0, 11);
158462306a36Sopenharmony_ci		UASM_i_MTC0(&p, a0, C0_CONTEXT);
158562306a36Sopenharmony_ci		uasm_i_jr(&p, 31);
158662306a36Sopenharmony_ci		uasm_i_ehb(&p);
158762306a36Sopenharmony_ci	} else {
158862306a36Sopenharmony_ci		/* PGD in c0_KScratch */
158962306a36Sopenharmony_ci		if (cpu_has_ldpte)
159062306a36Sopenharmony_ci			UASM_i_MTC0(&p, a0, C0_PWBASE);
159162306a36Sopenharmony_ci		else
159262306a36Sopenharmony_ci			UASM_i_MTC0(&p, a0, c0_kscratch(), pgd_reg);
159362306a36Sopenharmony_ci		uasm_i_jr(&p, 31);
159462306a36Sopenharmony_ci		uasm_i_ehb(&p);
159562306a36Sopenharmony_ci	}
159662306a36Sopenharmony_ci#else
159762306a36Sopenharmony_ci#ifdef CONFIG_SMP
159862306a36Sopenharmony_ci	/* Save PGD to pgd_current[smp_processor_id()] */
159962306a36Sopenharmony_ci	UASM_i_CPUID_MFC0(&p, a1, SMP_CPUID_REG);
160062306a36Sopenharmony_ci	UASM_i_SRL_SAFE(&p, a1, a1, SMP_CPUID_PTRSHIFT);
160162306a36Sopenharmony_ci	UASM_i_LA_mostly(&p, a2, pgdc);
160262306a36Sopenharmony_ci	UASM_i_ADDU(&p, a2, a2, a1);
160362306a36Sopenharmony_ci	UASM_i_SW(&p, a0, uasm_rel_lo(pgdc), a2);
160462306a36Sopenharmony_ci#else
160562306a36Sopenharmony_ci	UASM_i_LA_mostly(&p, a2, pgdc);
160662306a36Sopenharmony_ci	UASM_i_SW(&p, a0, uasm_rel_lo(pgdc), a2);
160762306a36Sopenharmony_ci#endif /* SMP */
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	/* if pgd_reg is allocated, save PGD also to scratch register */
161062306a36Sopenharmony_ci	if (pgd_reg != -1) {
161162306a36Sopenharmony_ci		UASM_i_MTC0(&p, a0, c0_kscratch(), pgd_reg);
161262306a36Sopenharmony_ci		uasm_i_jr(&p, 31);
161362306a36Sopenharmony_ci		uasm_i_ehb(&p);
161462306a36Sopenharmony_ci	} else {
161562306a36Sopenharmony_ci		uasm_i_jr(&p, 31);
161662306a36Sopenharmony_ci		uasm_i_nop(&p);
161762306a36Sopenharmony_ci	}
161862306a36Sopenharmony_ci#endif
161962306a36Sopenharmony_ci	if (p >= (u32 *)tlbmiss_handler_setup_pgd_end)
162062306a36Sopenharmony_ci		panic("tlbmiss_handler_setup_pgd space exceeded");
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
162362306a36Sopenharmony_ci	pr_debug("Wrote tlbmiss_handler_setup_pgd (%u instructions).\n",
162462306a36Sopenharmony_ci		 (unsigned int)(p - (u32 *)tlbmiss_handler_setup_pgd));
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci	dump_handler("tlbmiss_handler", tlbmiss_handler_setup_pgd,
162762306a36Sopenharmony_ci					tlbmiss_handler_setup_pgd_end);
162862306a36Sopenharmony_ci}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_cistatic void
163162306a36Sopenharmony_ciiPTE_LW(u32 **p, unsigned int pte, unsigned int ptr)
163262306a36Sopenharmony_ci{
163362306a36Sopenharmony_ci#ifdef CONFIG_SMP
163462306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
163562306a36Sopenharmony_ci		uasm_i_sync(p, 0);
163662306a36Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT
163762306a36Sopenharmony_ci	if (cpu_has_64bits)
163862306a36Sopenharmony_ci		uasm_i_lld(p, pte, 0, ptr);
163962306a36Sopenharmony_ci	else
164062306a36Sopenharmony_ci# endif
164162306a36Sopenharmony_ci		UASM_i_LL(p, pte, 0, ptr);
164262306a36Sopenharmony_ci#else
164362306a36Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT
164462306a36Sopenharmony_ci	if (cpu_has_64bits)
164562306a36Sopenharmony_ci		uasm_i_ld(p, pte, 0, ptr);
164662306a36Sopenharmony_ci	else
164762306a36Sopenharmony_ci# endif
164862306a36Sopenharmony_ci		UASM_i_LW(p, pte, 0, ptr);
164962306a36Sopenharmony_ci#endif
165062306a36Sopenharmony_ci}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_cistatic void
165362306a36Sopenharmony_ciiPTE_SW(u32 **p, struct uasm_reloc **r, unsigned int pte, unsigned int ptr,
165462306a36Sopenharmony_ci	unsigned int mode, unsigned int scratch)
165562306a36Sopenharmony_ci{
165662306a36Sopenharmony_ci	unsigned int hwmode = mode & (_PAGE_VALID | _PAGE_DIRTY);
165762306a36Sopenharmony_ci	unsigned int swmode = mode & ~hwmode;
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_XPA) && !cpu_has_64bits) {
166062306a36Sopenharmony_ci		uasm_i_lui(p, scratch, swmode >> 16);
166162306a36Sopenharmony_ci		uasm_i_or(p, pte, pte, scratch);
166262306a36Sopenharmony_ci		BUG_ON(swmode & 0xffff);
166362306a36Sopenharmony_ci	} else {
166462306a36Sopenharmony_ci		uasm_i_ori(p, pte, pte, mode);
166562306a36Sopenharmony_ci	}
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci#ifdef CONFIG_SMP
166862306a36Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT
166962306a36Sopenharmony_ci	if (cpu_has_64bits)
167062306a36Sopenharmony_ci		uasm_i_scd(p, pte, 0, ptr);
167162306a36Sopenharmony_ci	else
167262306a36Sopenharmony_ci# endif
167362306a36Sopenharmony_ci		UASM_i_SC(p, pte, 0, ptr);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (r10000_llsc_war())
167662306a36Sopenharmony_ci		uasm_il_beqzl(p, r, pte, label_smp_pgtable_change);
167762306a36Sopenharmony_ci	else
167862306a36Sopenharmony_ci		uasm_il_beqz(p, r, pte, label_smp_pgtable_change);
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT
168162306a36Sopenharmony_ci	if (!cpu_has_64bits) {
168262306a36Sopenharmony_ci		/* no uasm_i_nop needed */
168362306a36Sopenharmony_ci		uasm_i_ll(p, pte, sizeof(pte_t) / 2, ptr);
168462306a36Sopenharmony_ci		uasm_i_ori(p, pte, pte, hwmode);
168562306a36Sopenharmony_ci		BUG_ON(hwmode & ~0xffff);
168662306a36Sopenharmony_ci		uasm_i_sc(p, pte, sizeof(pte_t) / 2, ptr);
168762306a36Sopenharmony_ci		uasm_il_beqz(p, r, pte, label_smp_pgtable_change);
168862306a36Sopenharmony_ci		/* no uasm_i_nop needed */
168962306a36Sopenharmony_ci		uasm_i_lw(p, pte, 0, ptr);
169062306a36Sopenharmony_ci	} else
169162306a36Sopenharmony_ci		uasm_i_nop(p);
169262306a36Sopenharmony_ci# else
169362306a36Sopenharmony_ci	uasm_i_nop(p);
169462306a36Sopenharmony_ci# endif
169562306a36Sopenharmony_ci#else
169662306a36Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT
169762306a36Sopenharmony_ci	if (cpu_has_64bits)
169862306a36Sopenharmony_ci		uasm_i_sd(p, pte, 0, ptr);
169962306a36Sopenharmony_ci	else
170062306a36Sopenharmony_ci# endif
170162306a36Sopenharmony_ci		UASM_i_SW(p, pte, 0, ptr);
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci# ifdef CONFIG_PHYS_ADDR_T_64BIT
170462306a36Sopenharmony_ci	if (!cpu_has_64bits) {
170562306a36Sopenharmony_ci		uasm_i_lw(p, pte, sizeof(pte_t) / 2, ptr);
170662306a36Sopenharmony_ci		uasm_i_ori(p, pte, pte, hwmode);
170762306a36Sopenharmony_ci		BUG_ON(hwmode & ~0xffff);
170862306a36Sopenharmony_ci		uasm_i_sw(p, pte, sizeof(pte_t) / 2, ptr);
170962306a36Sopenharmony_ci		uasm_i_lw(p, pte, 0, ptr);
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci# endif
171262306a36Sopenharmony_ci#endif
171362306a36Sopenharmony_ci}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci/*
171662306a36Sopenharmony_ci * Check if PTE is present, if not then jump to LABEL. PTR points to
171762306a36Sopenharmony_ci * the page table where this PTE is located, PTE will be re-loaded
171862306a36Sopenharmony_ci * with it's original value.
171962306a36Sopenharmony_ci */
172062306a36Sopenharmony_cistatic void
172162306a36Sopenharmony_cibuild_pte_present(u32 **p, struct uasm_reloc **r,
172262306a36Sopenharmony_ci		  int pte, int ptr, int scratch, enum label_id lid)
172362306a36Sopenharmony_ci{
172462306a36Sopenharmony_ci	int t = scratch >= 0 ? scratch : pte;
172562306a36Sopenharmony_ci	int cur = pte;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	if (cpu_has_rixi) {
172862306a36Sopenharmony_ci		if (use_bbit_insns()) {
172962306a36Sopenharmony_ci			uasm_il_bbit0(p, r, pte, ilog2(_PAGE_PRESENT), lid);
173062306a36Sopenharmony_ci			uasm_i_nop(p);
173162306a36Sopenharmony_ci		} else {
173262306a36Sopenharmony_ci			if (_PAGE_PRESENT_SHIFT) {
173362306a36Sopenharmony_ci				uasm_i_srl(p, t, cur, _PAGE_PRESENT_SHIFT);
173462306a36Sopenharmony_ci				cur = t;
173562306a36Sopenharmony_ci			}
173662306a36Sopenharmony_ci			uasm_i_andi(p, t, cur, 1);
173762306a36Sopenharmony_ci			uasm_il_beqz(p, r, t, lid);
173862306a36Sopenharmony_ci			if (pte == t)
173962306a36Sopenharmony_ci				/* You lose the SMP race :-(*/
174062306a36Sopenharmony_ci				iPTE_LW(p, pte, ptr);
174162306a36Sopenharmony_ci		}
174262306a36Sopenharmony_ci	} else {
174362306a36Sopenharmony_ci		if (_PAGE_PRESENT_SHIFT) {
174462306a36Sopenharmony_ci			uasm_i_srl(p, t, cur, _PAGE_PRESENT_SHIFT);
174562306a36Sopenharmony_ci			cur = t;
174662306a36Sopenharmony_ci		}
174762306a36Sopenharmony_ci		uasm_i_andi(p, t, cur,
174862306a36Sopenharmony_ci			(_PAGE_PRESENT | _PAGE_NO_READ) >> _PAGE_PRESENT_SHIFT);
174962306a36Sopenharmony_ci		uasm_i_xori(p, t, t, _PAGE_PRESENT >> _PAGE_PRESENT_SHIFT);
175062306a36Sopenharmony_ci		uasm_il_bnez(p, r, t, lid);
175162306a36Sopenharmony_ci		if (pte == t)
175262306a36Sopenharmony_ci			/* You lose the SMP race :-(*/
175362306a36Sopenharmony_ci			iPTE_LW(p, pte, ptr);
175462306a36Sopenharmony_ci	}
175562306a36Sopenharmony_ci}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci/* Make PTE valid, store result in PTR. */
175862306a36Sopenharmony_cistatic void
175962306a36Sopenharmony_cibuild_make_valid(u32 **p, struct uasm_reloc **r, unsigned int pte,
176062306a36Sopenharmony_ci		 unsigned int ptr, unsigned int scratch)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	unsigned int mode = _PAGE_VALID | _PAGE_ACCESSED;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	iPTE_SW(p, r, pte, ptr, mode, scratch);
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci/*
176862306a36Sopenharmony_ci * Check if PTE can be written to, if not branch to LABEL. Regardless
176962306a36Sopenharmony_ci * restore PTE with value from PTR when done.
177062306a36Sopenharmony_ci */
177162306a36Sopenharmony_cistatic void
177262306a36Sopenharmony_cibuild_pte_writable(u32 **p, struct uasm_reloc **r,
177362306a36Sopenharmony_ci		   unsigned int pte, unsigned int ptr, int scratch,
177462306a36Sopenharmony_ci		   enum label_id lid)
177562306a36Sopenharmony_ci{
177662306a36Sopenharmony_ci	int t = scratch >= 0 ? scratch : pte;
177762306a36Sopenharmony_ci	int cur = pte;
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	if (_PAGE_PRESENT_SHIFT) {
178062306a36Sopenharmony_ci		uasm_i_srl(p, t, cur, _PAGE_PRESENT_SHIFT);
178162306a36Sopenharmony_ci		cur = t;
178262306a36Sopenharmony_ci	}
178362306a36Sopenharmony_ci	uasm_i_andi(p, t, cur,
178462306a36Sopenharmony_ci		    (_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT);
178562306a36Sopenharmony_ci	uasm_i_xori(p, t, t,
178662306a36Sopenharmony_ci		    (_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT);
178762306a36Sopenharmony_ci	uasm_il_bnez(p, r, t, lid);
178862306a36Sopenharmony_ci	if (pte == t)
178962306a36Sopenharmony_ci		/* You lose the SMP race :-(*/
179062306a36Sopenharmony_ci		iPTE_LW(p, pte, ptr);
179162306a36Sopenharmony_ci	else
179262306a36Sopenharmony_ci		uasm_i_nop(p);
179362306a36Sopenharmony_ci}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci/* Make PTE writable, update software status bits as well, then store
179662306a36Sopenharmony_ci * at PTR.
179762306a36Sopenharmony_ci */
179862306a36Sopenharmony_cistatic void
179962306a36Sopenharmony_cibuild_make_write(u32 **p, struct uasm_reloc **r, unsigned int pte,
180062306a36Sopenharmony_ci		 unsigned int ptr, unsigned int scratch)
180162306a36Sopenharmony_ci{
180262306a36Sopenharmony_ci	unsigned int mode = (_PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID
180362306a36Sopenharmony_ci			     | _PAGE_DIRTY);
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	iPTE_SW(p, r, pte, ptr, mode, scratch);
180662306a36Sopenharmony_ci}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci/*
180962306a36Sopenharmony_ci * Check if PTE can be modified, if not branch to LABEL. Regardless
181062306a36Sopenharmony_ci * restore PTE with value from PTR when done.
181162306a36Sopenharmony_ci */
181262306a36Sopenharmony_cistatic void
181362306a36Sopenharmony_cibuild_pte_modifiable(u32 **p, struct uasm_reloc **r,
181462306a36Sopenharmony_ci		     unsigned int pte, unsigned int ptr, int scratch,
181562306a36Sopenharmony_ci		     enum label_id lid)
181662306a36Sopenharmony_ci{
181762306a36Sopenharmony_ci	if (use_bbit_insns()) {
181862306a36Sopenharmony_ci		uasm_il_bbit0(p, r, pte, ilog2(_PAGE_WRITE), lid);
181962306a36Sopenharmony_ci		uasm_i_nop(p);
182062306a36Sopenharmony_ci	} else {
182162306a36Sopenharmony_ci		int t = scratch >= 0 ? scratch : pte;
182262306a36Sopenharmony_ci		uasm_i_srl(p, t, pte, _PAGE_WRITE_SHIFT);
182362306a36Sopenharmony_ci		uasm_i_andi(p, t, t, 1);
182462306a36Sopenharmony_ci		uasm_il_beqz(p, r, t, lid);
182562306a36Sopenharmony_ci		if (pte == t)
182662306a36Sopenharmony_ci			/* You lose the SMP race :-(*/
182762306a36Sopenharmony_ci			iPTE_LW(p, pte, ptr);
182862306a36Sopenharmony_ci	}
182962306a36Sopenharmony_ci}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci/*
183562306a36Sopenharmony_ci * R3000 style TLB load/store/modify handlers.
183662306a36Sopenharmony_ci */
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci/*
183962306a36Sopenharmony_ci * This places the pte into ENTRYLO0 and writes it with tlbwi.
184062306a36Sopenharmony_ci * Then it returns.
184162306a36Sopenharmony_ci */
184262306a36Sopenharmony_cistatic void
184362306a36Sopenharmony_cibuild_r3000_pte_reload_tlbwi(u32 **p, unsigned int pte, unsigned int tmp)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	uasm_i_mtc0(p, pte, C0_ENTRYLO0); /* cp0 delay */
184662306a36Sopenharmony_ci	uasm_i_mfc0(p, tmp, C0_EPC); /* cp0 delay */
184762306a36Sopenharmony_ci	uasm_i_tlbwi(p);
184862306a36Sopenharmony_ci	uasm_i_jr(p, tmp);
184962306a36Sopenharmony_ci	uasm_i_rfe(p); /* branch delay */
185062306a36Sopenharmony_ci}
185162306a36Sopenharmony_ci
185262306a36Sopenharmony_ci/*
185362306a36Sopenharmony_ci * This places the pte into ENTRYLO0 and writes it with tlbwi
185462306a36Sopenharmony_ci * or tlbwr as appropriate.  This is because the index register
185562306a36Sopenharmony_ci * may have the probe fail bit set as a result of a trap on a
185662306a36Sopenharmony_ci * kseg2 access, i.e. without refill.  Then it returns.
185762306a36Sopenharmony_ci */
185862306a36Sopenharmony_cistatic void
185962306a36Sopenharmony_cibuild_r3000_tlb_reload_write(u32 **p, struct uasm_label **l,
186062306a36Sopenharmony_ci			     struct uasm_reloc **r, unsigned int pte,
186162306a36Sopenharmony_ci			     unsigned int tmp)
186262306a36Sopenharmony_ci{
186362306a36Sopenharmony_ci	uasm_i_mfc0(p, tmp, C0_INDEX);
186462306a36Sopenharmony_ci	uasm_i_mtc0(p, pte, C0_ENTRYLO0); /* cp0 delay */
186562306a36Sopenharmony_ci	uasm_il_bltz(p, r, tmp, label_r3000_write_probe_fail); /* cp0 delay */
186662306a36Sopenharmony_ci	uasm_i_mfc0(p, tmp, C0_EPC); /* branch delay */
186762306a36Sopenharmony_ci	uasm_i_tlbwi(p); /* cp0 delay */
186862306a36Sopenharmony_ci	uasm_i_jr(p, tmp);
186962306a36Sopenharmony_ci	uasm_i_rfe(p); /* branch delay */
187062306a36Sopenharmony_ci	uasm_l_r3000_write_probe_fail(l, *p);
187162306a36Sopenharmony_ci	uasm_i_tlbwr(p); /* cp0 delay */
187262306a36Sopenharmony_ci	uasm_i_jr(p, tmp);
187362306a36Sopenharmony_ci	uasm_i_rfe(p); /* branch delay */
187462306a36Sopenharmony_ci}
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_cistatic void
187762306a36Sopenharmony_cibuild_r3000_tlbchange_handler_head(u32 **p, unsigned int pte,
187862306a36Sopenharmony_ci				   unsigned int ptr)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	long pgdc = (long)pgd_current;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	uasm_i_mfc0(p, pte, C0_BADVADDR);
188362306a36Sopenharmony_ci	uasm_i_lui(p, ptr, uasm_rel_hi(pgdc)); /* cp0 delay */
188462306a36Sopenharmony_ci	uasm_i_lw(p, ptr, uasm_rel_lo(pgdc), ptr);
188562306a36Sopenharmony_ci	uasm_i_srl(p, pte, pte, 22); /* load delay */
188662306a36Sopenharmony_ci	uasm_i_sll(p, pte, pte, 2);
188762306a36Sopenharmony_ci	uasm_i_addu(p, ptr, ptr, pte);
188862306a36Sopenharmony_ci	uasm_i_mfc0(p, pte, C0_CONTEXT);
188962306a36Sopenharmony_ci	uasm_i_lw(p, ptr, 0, ptr); /* cp0 delay */
189062306a36Sopenharmony_ci	uasm_i_andi(p, pte, pte, 0xffc); /* load delay */
189162306a36Sopenharmony_ci	uasm_i_addu(p, ptr, ptr, pte);
189262306a36Sopenharmony_ci	uasm_i_lw(p, pte, 0, ptr);
189362306a36Sopenharmony_ci	uasm_i_tlbp(p); /* load delay */
189462306a36Sopenharmony_ci}
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_cistatic void build_r3000_tlb_load_handler(void)
189762306a36Sopenharmony_ci{
189862306a36Sopenharmony_ci	u32 *p = (u32 *)handle_tlbl;
189962306a36Sopenharmony_ci	struct uasm_label *l = labels;
190062306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci	memset(p, 0, handle_tlbl_end - (char *)p);
190362306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
190462306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	build_r3000_tlbchange_handler_head(&p, K0, K1);
190762306a36Sopenharmony_ci	build_pte_present(&p, &r, K0, K1, -1, label_nopage_tlbl);
190862306a36Sopenharmony_ci	uasm_i_nop(&p); /* load delay */
190962306a36Sopenharmony_ci	build_make_valid(&p, &r, K0, K1, -1);
191062306a36Sopenharmony_ci	build_r3000_tlb_reload_write(&p, &l, &r, K0, K1);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci	uasm_l_nopage_tlbl(&l, p);
191362306a36Sopenharmony_ci	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff);
191462306a36Sopenharmony_ci	uasm_i_nop(&p);
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_ci	if (p >= (u32 *)handle_tlbl_end)
191762306a36Sopenharmony_ci		panic("TLB load handler fastpath space exceeded");
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
192062306a36Sopenharmony_ci	pr_debug("Wrote TLB load handler fastpath (%u instructions).\n",
192162306a36Sopenharmony_ci		 (unsigned int)(p - (u32 *)handle_tlbl));
192262306a36Sopenharmony_ci
192362306a36Sopenharmony_ci	dump_handler("r3000_tlb_load", handle_tlbl, handle_tlbl_end);
192462306a36Sopenharmony_ci}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_cistatic void build_r3000_tlb_store_handler(void)
192762306a36Sopenharmony_ci{
192862306a36Sopenharmony_ci	u32 *p = (u32 *)handle_tlbs;
192962306a36Sopenharmony_ci	struct uasm_label *l = labels;
193062306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	memset(p, 0, handle_tlbs_end - (char *)p);
193362306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
193462306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	build_r3000_tlbchange_handler_head(&p, K0, K1);
193762306a36Sopenharmony_ci	build_pte_writable(&p, &r, K0, K1, -1, label_nopage_tlbs);
193862306a36Sopenharmony_ci	uasm_i_nop(&p); /* load delay */
193962306a36Sopenharmony_ci	build_make_write(&p, &r, K0, K1, -1);
194062306a36Sopenharmony_ci	build_r3000_tlb_reload_write(&p, &l, &r, K0, K1);
194162306a36Sopenharmony_ci
194262306a36Sopenharmony_ci	uasm_l_nopage_tlbs(&l, p);
194362306a36Sopenharmony_ci	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
194462306a36Sopenharmony_ci	uasm_i_nop(&p);
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	if (p >= (u32 *)handle_tlbs_end)
194762306a36Sopenharmony_ci		panic("TLB store handler fastpath space exceeded");
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
195062306a36Sopenharmony_ci	pr_debug("Wrote TLB store handler fastpath (%u instructions).\n",
195162306a36Sopenharmony_ci		 (unsigned int)(p - (u32 *)handle_tlbs));
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci	dump_handler("r3000_tlb_store", handle_tlbs, handle_tlbs_end);
195462306a36Sopenharmony_ci}
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_cistatic void build_r3000_tlb_modify_handler(void)
195762306a36Sopenharmony_ci{
195862306a36Sopenharmony_ci	u32 *p = (u32 *)handle_tlbm;
195962306a36Sopenharmony_ci	struct uasm_label *l = labels;
196062306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	memset(p, 0, handle_tlbm_end - (char *)p);
196362306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
196462306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci	build_r3000_tlbchange_handler_head(&p, K0, K1);
196762306a36Sopenharmony_ci	build_pte_modifiable(&p, &r, K0, K1,  -1, label_nopage_tlbm);
196862306a36Sopenharmony_ci	uasm_i_nop(&p); /* load delay */
196962306a36Sopenharmony_ci	build_make_write(&p, &r, K0, K1, -1);
197062306a36Sopenharmony_ci	build_r3000_pte_reload_tlbwi(&p, K0, K1);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	uasm_l_nopage_tlbm(&l, p);
197362306a36Sopenharmony_ci	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
197462306a36Sopenharmony_ci	uasm_i_nop(&p);
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	if (p >= (u32 *)handle_tlbm_end)
197762306a36Sopenharmony_ci		panic("TLB modify handler fastpath space exceeded");
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
198062306a36Sopenharmony_ci	pr_debug("Wrote TLB modify handler fastpath (%u instructions).\n",
198162306a36Sopenharmony_ci		 (unsigned int)(p - (u32 *)handle_tlbm));
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	dump_handler("r3000_tlb_modify", handle_tlbm, handle_tlbm_end);
198462306a36Sopenharmony_ci}
198562306a36Sopenharmony_ci#endif /* CONFIG_MIPS_PGD_C0_CONTEXT */
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_cistatic bool cpu_has_tlbex_tlbp_race(void)
198862306a36Sopenharmony_ci{
198962306a36Sopenharmony_ci	/*
199062306a36Sopenharmony_ci	 * When a Hardware Table Walker is running it can replace TLB entries
199162306a36Sopenharmony_ci	 * at any time, leading to a race between it & the CPU.
199262306a36Sopenharmony_ci	 */
199362306a36Sopenharmony_ci	if (cpu_has_htw)
199462306a36Sopenharmony_ci		return true;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	/*
199762306a36Sopenharmony_ci	 * If the CPU shares FTLB RAM with its siblings then our entry may be
199862306a36Sopenharmony_ci	 * replaced at any time by a sibling performing a write to the FTLB.
199962306a36Sopenharmony_ci	 */
200062306a36Sopenharmony_ci	if (cpu_has_shared_ftlb_ram)
200162306a36Sopenharmony_ci		return true;
200262306a36Sopenharmony_ci
200362306a36Sopenharmony_ci	/* In all other cases there ought to be no race condition to handle */
200462306a36Sopenharmony_ci	return false;
200562306a36Sopenharmony_ci}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci/*
200862306a36Sopenharmony_ci * R4000 style TLB load/store/modify handlers.
200962306a36Sopenharmony_ci */
201062306a36Sopenharmony_cistatic struct work_registers
201162306a36Sopenharmony_cibuild_r4000_tlbchange_handler_head(u32 **p, struct uasm_label **l,
201262306a36Sopenharmony_ci				   struct uasm_reloc **r)
201362306a36Sopenharmony_ci{
201462306a36Sopenharmony_ci	struct work_registers wr = build_get_work_registers(p);
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci#ifdef CONFIG_64BIT
201762306a36Sopenharmony_ci	build_get_pmde64(p, l, r, wr.r1, wr.r2); /* get pmd in ptr */
201862306a36Sopenharmony_ci#else
201962306a36Sopenharmony_ci	build_get_pgde32(p, wr.r1, wr.r2); /* get pgd in ptr */
202062306a36Sopenharmony_ci#endif
202162306a36Sopenharmony_ci
202262306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
202362306a36Sopenharmony_ci	/*
202462306a36Sopenharmony_ci	 * For huge tlb entries, pmd doesn't contain an address but
202562306a36Sopenharmony_ci	 * instead contains the tlb pte. Check the PAGE_HUGE bit and
202662306a36Sopenharmony_ci	 * see if we need to jump to huge tlb processing.
202762306a36Sopenharmony_ci	 */
202862306a36Sopenharmony_ci	build_is_huge_pte(p, r, wr.r1, wr.r2, label_tlb_huge_update);
202962306a36Sopenharmony_ci#endif
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	UASM_i_MFC0(p, wr.r1, C0_BADVADDR);
203262306a36Sopenharmony_ci	UASM_i_LW(p, wr.r2, 0, wr.r2);
203362306a36Sopenharmony_ci	UASM_i_SRL(p, wr.r1, wr.r1, PAGE_SHIFT - PTE_T_LOG2);
203462306a36Sopenharmony_ci	uasm_i_andi(p, wr.r1, wr.r1, (PTRS_PER_PTE - 1) << PTE_T_LOG2);
203562306a36Sopenharmony_ci	UASM_i_ADDU(p, wr.r2, wr.r2, wr.r1);
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci#ifdef CONFIG_SMP
203862306a36Sopenharmony_ci	uasm_l_smp_pgtable_change(l, *p);
203962306a36Sopenharmony_ci#endif
204062306a36Sopenharmony_ci	iPTE_LW(p, wr.r1, wr.r2); /* get even pte */
204162306a36Sopenharmony_ci	if (!m4kc_tlbp_war()) {
204262306a36Sopenharmony_ci		build_tlb_probe_entry(p);
204362306a36Sopenharmony_ci		if (cpu_has_tlbex_tlbp_race()) {
204462306a36Sopenharmony_ci			/* race condition happens, leaving */
204562306a36Sopenharmony_ci			uasm_i_ehb(p);
204662306a36Sopenharmony_ci			uasm_i_mfc0(p, wr.r3, C0_INDEX);
204762306a36Sopenharmony_ci			uasm_il_bltz(p, r, wr.r3, label_leave);
204862306a36Sopenharmony_ci			uasm_i_nop(p);
204962306a36Sopenharmony_ci		}
205062306a36Sopenharmony_ci	}
205162306a36Sopenharmony_ci	return wr;
205262306a36Sopenharmony_ci}
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_cistatic void
205562306a36Sopenharmony_cibuild_r4000_tlbchange_handler_tail(u32 **p, struct uasm_label **l,
205662306a36Sopenharmony_ci				   struct uasm_reloc **r, unsigned int tmp,
205762306a36Sopenharmony_ci				   unsigned int ptr)
205862306a36Sopenharmony_ci{
205962306a36Sopenharmony_ci	uasm_i_ori(p, ptr, ptr, sizeof(pte_t));
206062306a36Sopenharmony_ci	uasm_i_xori(p, ptr, ptr, sizeof(pte_t));
206162306a36Sopenharmony_ci	build_update_entries(p, tmp, ptr);
206262306a36Sopenharmony_ci	build_tlb_write_entry(p, l, r, tlb_indexed);
206362306a36Sopenharmony_ci	uasm_l_leave(l, *p);
206462306a36Sopenharmony_ci	build_restore_work_registers(p);
206562306a36Sopenharmony_ci	uasm_i_eret(p); /* return from trap */
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci#ifdef CONFIG_64BIT
206862306a36Sopenharmony_ci	build_get_pgd_vmalloc64(p, l, r, tmp, ptr, not_refill);
206962306a36Sopenharmony_ci#endif
207062306a36Sopenharmony_ci}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_cistatic void build_r4000_tlb_load_handler(void)
207362306a36Sopenharmony_ci{
207462306a36Sopenharmony_ci	u32 *p = (u32 *)msk_isa16_mode((ulong)handle_tlbl);
207562306a36Sopenharmony_ci	struct uasm_label *l = labels;
207662306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
207762306a36Sopenharmony_ci	struct work_registers wr;
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	memset(p, 0, handle_tlbl_end - (char *)p);
208062306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
208162306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci	if (bcm1250_m3_war()) {
208462306a36Sopenharmony_ci		unsigned int segbits = 44;
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci		uasm_i_dmfc0(&p, K0, C0_BADVADDR);
208762306a36Sopenharmony_ci		uasm_i_dmfc0(&p, K1, C0_ENTRYHI);
208862306a36Sopenharmony_ci		uasm_i_xor(&p, K0, K0, K1);
208962306a36Sopenharmony_ci		uasm_i_dsrl_safe(&p, K1, K0, 62);
209062306a36Sopenharmony_ci		uasm_i_dsrl_safe(&p, K0, K0, 12 + 1);
209162306a36Sopenharmony_ci		uasm_i_dsll_safe(&p, K0, K0, 64 + 12 + 1 - segbits);
209262306a36Sopenharmony_ci		uasm_i_or(&p, K0, K0, K1);
209362306a36Sopenharmony_ci		uasm_il_bnez(&p, &r, K0, label_leave);
209462306a36Sopenharmony_ci		/* No need for uasm_i_nop */
209562306a36Sopenharmony_ci	}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	wr = build_r4000_tlbchange_handler_head(&p, &l, &r);
209862306a36Sopenharmony_ci	build_pte_present(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbl);
209962306a36Sopenharmony_ci	if (m4kc_tlbp_war())
210062306a36Sopenharmony_ci		build_tlb_probe_entry(&p);
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	if (cpu_has_rixi && !cpu_has_rixiex) {
210362306a36Sopenharmony_ci		/*
210462306a36Sopenharmony_ci		 * If the page is not _PAGE_VALID, RI or XI could not
210562306a36Sopenharmony_ci		 * have triggered it.  Skip the expensive test..
210662306a36Sopenharmony_ci		 */
210762306a36Sopenharmony_ci		if (use_bbit_insns()) {
210862306a36Sopenharmony_ci			uasm_il_bbit0(&p, &r, wr.r1, ilog2(_PAGE_VALID),
210962306a36Sopenharmony_ci				      label_tlbl_goaround1);
211062306a36Sopenharmony_ci		} else {
211162306a36Sopenharmony_ci			uasm_i_andi(&p, wr.r3, wr.r1, _PAGE_VALID);
211262306a36Sopenharmony_ci			uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround1);
211362306a36Sopenharmony_ci		}
211462306a36Sopenharmony_ci		uasm_i_nop(&p);
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci		/*
211762306a36Sopenharmony_ci		 * Warn if something may race with us & replace the TLB entry
211862306a36Sopenharmony_ci		 * before we read it here. Everything with such races should
211962306a36Sopenharmony_ci		 * also have dedicated RiXi exception handlers, so this
212062306a36Sopenharmony_ci		 * shouldn't be hit.
212162306a36Sopenharmony_ci		 */
212262306a36Sopenharmony_ci		WARN(cpu_has_tlbex_tlbp_race(), "Unhandled race in RiXi path");
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci		uasm_i_tlbr(&p);
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci		if (cpu_has_mips_r2_exec_hazard)
212762306a36Sopenharmony_ci			uasm_i_ehb(&p);
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci		/* Examine  entrylo 0 or 1 based on ptr. */
213062306a36Sopenharmony_ci		if (use_bbit_insns()) {
213162306a36Sopenharmony_ci			uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8);
213262306a36Sopenharmony_ci		} else {
213362306a36Sopenharmony_ci			uasm_i_andi(&p, wr.r3, wr.r2, sizeof(pte_t));
213462306a36Sopenharmony_ci			uasm_i_beqz(&p, wr.r3, 8);
213562306a36Sopenharmony_ci		}
213662306a36Sopenharmony_ci		/* load it in the delay slot*/
213762306a36Sopenharmony_ci		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO0);
213862306a36Sopenharmony_ci		/* load it if ptr is odd */
213962306a36Sopenharmony_ci		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO1);
214062306a36Sopenharmony_ci		/*
214162306a36Sopenharmony_ci		 * If the entryLo (now in wr.r3) is valid (bit 1), RI or
214262306a36Sopenharmony_ci		 * XI must have triggered it.
214362306a36Sopenharmony_ci		 */
214462306a36Sopenharmony_ci		if (use_bbit_insns()) {
214562306a36Sopenharmony_ci			uasm_il_bbit1(&p, &r, wr.r3, 1, label_nopage_tlbl);
214662306a36Sopenharmony_ci			uasm_i_nop(&p);
214762306a36Sopenharmony_ci			uasm_l_tlbl_goaround1(&l, p);
214862306a36Sopenharmony_ci		} else {
214962306a36Sopenharmony_ci			uasm_i_andi(&p, wr.r3, wr.r3, 2);
215062306a36Sopenharmony_ci			uasm_il_bnez(&p, &r, wr.r3, label_nopage_tlbl);
215162306a36Sopenharmony_ci			uasm_i_nop(&p);
215262306a36Sopenharmony_ci		}
215362306a36Sopenharmony_ci		uasm_l_tlbl_goaround1(&l, p);
215462306a36Sopenharmony_ci	}
215562306a36Sopenharmony_ci	build_make_valid(&p, &r, wr.r1, wr.r2, wr.r3);
215662306a36Sopenharmony_ci	build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2);
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
215962306a36Sopenharmony_ci	/*
216062306a36Sopenharmony_ci	 * This is the entry point when build_r4000_tlbchange_handler_head
216162306a36Sopenharmony_ci	 * spots a huge page.
216262306a36Sopenharmony_ci	 */
216362306a36Sopenharmony_ci	uasm_l_tlb_huge_update(&l, p);
216462306a36Sopenharmony_ci	iPTE_LW(&p, wr.r1, wr.r2);
216562306a36Sopenharmony_ci	build_pte_present(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbl);
216662306a36Sopenharmony_ci	build_tlb_probe_entry(&p);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	if (cpu_has_rixi && !cpu_has_rixiex) {
216962306a36Sopenharmony_ci		/*
217062306a36Sopenharmony_ci		 * If the page is not _PAGE_VALID, RI or XI could not
217162306a36Sopenharmony_ci		 * have triggered it.  Skip the expensive test..
217262306a36Sopenharmony_ci		 */
217362306a36Sopenharmony_ci		if (use_bbit_insns()) {
217462306a36Sopenharmony_ci			uasm_il_bbit0(&p, &r, wr.r1, ilog2(_PAGE_VALID),
217562306a36Sopenharmony_ci				      label_tlbl_goaround2);
217662306a36Sopenharmony_ci		} else {
217762306a36Sopenharmony_ci			uasm_i_andi(&p, wr.r3, wr.r1, _PAGE_VALID);
217862306a36Sopenharmony_ci			uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround2);
217962306a36Sopenharmony_ci		}
218062306a36Sopenharmony_ci		uasm_i_nop(&p);
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci		/*
218362306a36Sopenharmony_ci		 * Warn if something may race with us & replace the TLB entry
218462306a36Sopenharmony_ci		 * before we read it here. Everything with such races should
218562306a36Sopenharmony_ci		 * also have dedicated RiXi exception handlers, so this
218662306a36Sopenharmony_ci		 * shouldn't be hit.
218762306a36Sopenharmony_ci		 */
218862306a36Sopenharmony_ci		WARN(cpu_has_tlbex_tlbp_race(), "Unhandled race in RiXi path");
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci		uasm_i_tlbr(&p);
219162306a36Sopenharmony_ci
219262306a36Sopenharmony_ci		if (cpu_has_mips_r2_exec_hazard)
219362306a36Sopenharmony_ci			uasm_i_ehb(&p);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci		/* Examine  entrylo 0 or 1 based on ptr. */
219662306a36Sopenharmony_ci		if (use_bbit_insns()) {
219762306a36Sopenharmony_ci			uasm_i_bbit0(&p, wr.r2, ilog2(sizeof(pte_t)), 8);
219862306a36Sopenharmony_ci		} else {
219962306a36Sopenharmony_ci			uasm_i_andi(&p, wr.r3, wr.r2, sizeof(pte_t));
220062306a36Sopenharmony_ci			uasm_i_beqz(&p, wr.r3, 8);
220162306a36Sopenharmony_ci		}
220262306a36Sopenharmony_ci		/* load it in the delay slot*/
220362306a36Sopenharmony_ci		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO0);
220462306a36Sopenharmony_ci		/* load it if ptr is odd */
220562306a36Sopenharmony_ci		UASM_i_MFC0(&p, wr.r3, C0_ENTRYLO1);
220662306a36Sopenharmony_ci		/*
220762306a36Sopenharmony_ci		 * If the entryLo (now in wr.r3) is valid (bit 1), RI or
220862306a36Sopenharmony_ci		 * XI must have triggered it.
220962306a36Sopenharmony_ci		 */
221062306a36Sopenharmony_ci		if (use_bbit_insns()) {
221162306a36Sopenharmony_ci			uasm_il_bbit0(&p, &r, wr.r3, 1, label_tlbl_goaround2);
221262306a36Sopenharmony_ci		} else {
221362306a36Sopenharmony_ci			uasm_i_andi(&p, wr.r3, wr.r3, 2);
221462306a36Sopenharmony_ci			uasm_il_beqz(&p, &r, wr.r3, label_tlbl_goaround2);
221562306a36Sopenharmony_ci		}
221662306a36Sopenharmony_ci		if (PM_DEFAULT_MASK == 0)
221762306a36Sopenharmony_ci			uasm_i_nop(&p);
221862306a36Sopenharmony_ci		/*
221962306a36Sopenharmony_ci		 * We clobbered C0_PAGEMASK, restore it.  On the other branch
222062306a36Sopenharmony_ci		 * it is restored in build_huge_tlb_write_entry.
222162306a36Sopenharmony_ci		 */
222262306a36Sopenharmony_ci		build_restore_pagemask(&p, &r, wr.r3, label_nopage_tlbl, 0);
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci		uasm_l_tlbl_goaround2(&l, p);
222562306a36Sopenharmony_ci	}
222662306a36Sopenharmony_ci	uasm_i_ori(&p, wr.r1, wr.r1, (_PAGE_ACCESSED | _PAGE_VALID));
222762306a36Sopenharmony_ci	build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1);
222862306a36Sopenharmony_ci#endif
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	uasm_l_nopage_tlbl(&l, p);
223162306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
223262306a36Sopenharmony_ci		uasm_i_sync(&p, 0);
223362306a36Sopenharmony_ci	build_restore_work_registers(&p);
223462306a36Sopenharmony_ci#ifdef CONFIG_CPU_MICROMIPS
223562306a36Sopenharmony_ci	if ((unsigned long)tlb_do_page_fault_0 & 1) {
223662306a36Sopenharmony_ci		uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_0));
223762306a36Sopenharmony_ci		uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_0));
223862306a36Sopenharmony_ci		uasm_i_jr(&p, K0);
223962306a36Sopenharmony_ci	} else
224062306a36Sopenharmony_ci#endif
224162306a36Sopenharmony_ci	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_0 & 0x0fffffff);
224262306a36Sopenharmony_ci	uasm_i_nop(&p);
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	if (p >= (u32 *)handle_tlbl_end)
224562306a36Sopenharmony_ci		panic("TLB load handler fastpath space exceeded");
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
224862306a36Sopenharmony_ci	pr_debug("Wrote TLB load handler fastpath (%u instructions).\n",
224962306a36Sopenharmony_ci		 (unsigned int)(p - (u32 *)handle_tlbl));
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	dump_handler("r4000_tlb_load", handle_tlbl, handle_tlbl_end);
225262306a36Sopenharmony_ci}
225362306a36Sopenharmony_ci
225462306a36Sopenharmony_cistatic void build_r4000_tlb_store_handler(void)
225562306a36Sopenharmony_ci{
225662306a36Sopenharmony_ci	u32 *p = (u32 *)msk_isa16_mode((ulong)handle_tlbs);
225762306a36Sopenharmony_ci	struct uasm_label *l = labels;
225862306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
225962306a36Sopenharmony_ci	struct work_registers wr;
226062306a36Sopenharmony_ci
226162306a36Sopenharmony_ci	memset(p, 0, handle_tlbs_end - (char *)p);
226262306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
226362306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci	wr = build_r4000_tlbchange_handler_head(&p, &l, &r);
226662306a36Sopenharmony_ci	build_pte_writable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbs);
226762306a36Sopenharmony_ci	if (m4kc_tlbp_war())
226862306a36Sopenharmony_ci		build_tlb_probe_entry(&p);
226962306a36Sopenharmony_ci	build_make_write(&p, &r, wr.r1, wr.r2, wr.r3);
227062306a36Sopenharmony_ci	build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2);
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
227362306a36Sopenharmony_ci	/*
227462306a36Sopenharmony_ci	 * This is the entry point when
227562306a36Sopenharmony_ci	 * build_r4000_tlbchange_handler_head spots a huge page.
227662306a36Sopenharmony_ci	 */
227762306a36Sopenharmony_ci	uasm_l_tlb_huge_update(&l, p);
227862306a36Sopenharmony_ci	iPTE_LW(&p, wr.r1, wr.r2);
227962306a36Sopenharmony_ci	build_pte_writable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbs);
228062306a36Sopenharmony_ci	build_tlb_probe_entry(&p);
228162306a36Sopenharmony_ci	uasm_i_ori(&p, wr.r1, wr.r1,
228262306a36Sopenharmony_ci		   _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
228362306a36Sopenharmony_ci	build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 1);
228462306a36Sopenharmony_ci#endif
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci	uasm_l_nopage_tlbs(&l, p);
228762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
228862306a36Sopenharmony_ci		uasm_i_sync(&p, 0);
228962306a36Sopenharmony_ci	build_restore_work_registers(&p);
229062306a36Sopenharmony_ci#ifdef CONFIG_CPU_MICROMIPS
229162306a36Sopenharmony_ci	if ((unsigned long)tlb_do_page_fault_1 & 1) {
229262306a36Sopenharmony_ci		uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_1));
229362306a36Sopenharmony_ci		uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_1));
229462306a36Sopenharmony_ci		uasm_i_jr(&p, K0);
229562306a36Sopenharmony_ci	} else
229662306a36Sopenharmony_ci#endif
229762306a36Sopenharmony_ci	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
229862306a36Sopenharmony_ci	uasm_i_nop(&p);
229962306a36Sopenharmony_ci
230062306a36Sopenharmony_ci	if (p >= (u32 *)handle_tlbs_end)
230162306a36Sopenharmony_ci		panic("TLB store handler fastpath space exceeded");
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
230462306a36Sopenharmony_ci	pr_debug("Wrote TLB store handler fastpath (%u instructions).\n",
230562306a36Sopenharmony_ci		 (unsigned int)(p - (u32 *)handle_tlbs));
230662306a36Sopenharmony_ci
230762306a36Sopenharmony_ci	dump_handler("r4000_tlb_store", handle_tlbs, handle_tlbs_end);
230862306a36Sopenharmony_ci}
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_cistatic void build_r4000_tlb_modify_handler(void)
231162306a36Sopenharmony_ci{
231262306a36Sopenharmony_ci	u32 *p = (u32 *)msk_isa16_mode((ulong)handle_tlbm);
231362306a36Sopenharmony_ci	struct uasm_label *l = labels;
231462306a36Sopenharmony_ci	struct uasm_reloc *r = relocs;
231562306a36Sopenharmony_ci	struct work_registers wr;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	memset(p, 0, handle_tlbm_end - (char *)p);
231862306a36Sopenharmony_ci	memset(labels, 0, sizeof(labels));
231962306a36Sopenharmony_ci	memset(relocs, 0, sizeof(relocs));
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	wr = build_r4000_tlbchange_handler_head(&p, &l, &r);
232262306a36Sopenharmony_ci	build_pte_modifiable(&p, &r, wr.r1, wr.r2, wr.r3, label_nopage_tlbm);
232362306a36Sopenharmony_ci	if (m4kc_tlbp_war())
232462306a36Sopenharmony_ci		build_tlb_probe_entry(&p);
232562306a36Sopenharmony_ci	/* Present and writable bits set, set accessed and dirty bits. */
232662306a36Sopenharmony_ci	build_make_write(&p, &r, wr.r1, wr.r2, wr.r3);
232762306a36Sopenharmony_ci	build_r4000_tlbchange_handler_tail(&p, &l, &r, wr.r1, wr.r2);
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci#ifdef CONFIG_MIPS_HUGE_TLB_SUPPORT
233062306a36Sopenharmony_ci	/*
233162306a36Sopenharmony_ci	 * This is the entry point when
233262306a36Sopenharmony_ci	 * build_r4000_tlbchange_handler_head spots a huge page.
233362306a36Sopenharmony_ci	 */
233462306a36Sopenharmony_ci	uasm_l_tlb_huge_update(&l, p);
233562306a36Sopenharmony_ci	iPTE_LW(&p, wr.r1, wr.r2);
233662306a36Sopenharmony_ci	build_pte_modifiable(&p, &r, wr.r1, wr.r2,  wr.r3, label_nopage_tlbm);
233762306a36Sopenharmony_ci	build_tlb_probe_entry(&p);
233862306a36Sopenharmony_ci	uasm_i_ori(&p, wr.r1, wr.r1,
233962306a36Sopenharmony_ci		   _PAGE_ACCESSED | _PAGE_MODIFIED | _PAGE_VALID | _PAGE_DIRTY);
234062306a36Sopenharmony_ci	build_huge_handler_tail(&p, &r, &l, wr.r1, wr.r2, 0);
234162306a36Sopenharmony_ci#endif
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	uasm_l_nopage_tlbm(&l, p);
234462306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_CPU_LOONGSON3_WORKAROUNDS))
234562306a36Sopenharmony_ci		uasm_i_sync(&p, 0);
234662306a36Sopenharmony_ci	build_restore_work_registers(&p);
234762306a36Sopenharmony_ci#ifdef CONFIG_CPU_MICROMIPS
234862306a36Sopenharmony_ci	if ((unsigned long)tlb_do_page_fault_1 & 1) {
234962306a36Sopenharmony_ci		uasm_i_lui(&p, K0, uasm_rel_hi((long)tlb_do_page_fault_1));
235062306a36Sopenharmony_ci		uasm_i_addiu(&p, K0, K0, uasm_rel_lo((long)tlb_do_page_fault_1));
235162306a36Sopenharmony_ci		uasm_i_jr(&p, K0);
235262306a36Sopenharmony_ci	} else
235362306a36Sopenharmony_ci#endif
235462306a36Sopenharmony_ci	uasm_i_j(&p, (unsigned long)tlb_do_page_fault_1 & 0x0fffffff);
235562306a36Sopenharmony_ci	uasm_i_nop(&p);
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci	if (p >= (u32 *)handle_tlbm_end)
235862306a36Sopenharmony_ci		panic("TLB modify handler fastpath space exceeded");
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	uasm_resolve_relocs(relocs, labels);
236162306a36Sopenharmony_ci	pr_debug("Wrote TLB modify handler fastpath (%u instructions).\n",
236262306a36Sopenharmony_ci		 (unsigned int)(p - (u32 *)handle_tlbm));
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	dump_handler("r4000_tlb_modify", handle_tlbm, handle_tlbm_end);
236562306a36Sopenharmony_ci}
236662306a36Sopenharmony_ci
236762306a36Sopenharmony_cistatic void flush_tlb_handlers(void)
236862306a36Sopenharmony_ci{
236962306a36Sopenharmony_ci	local_flush_icache_range((unsigned long)handle_tlbl,
237062306a36Sopenharmony_ci			   (unsigned long)handle_tlbl_end);
237162306a36Sopenharmony_ci	local_flush_icache_range((unsigned long)handle_tlbs,
237262306a36Sopenharmony_ci			   (unsigned long)handle_tlbs_end);
237362306a36Sopenharmony_ci	local_flush_icache_range((unsigned long)handle_tlbm,
237462306a36Sopenharmony_ci			   (unsigned long)handle_tlbm_end);
237562306a36Sopenharmony_ci	local_flush_icache_range((unsigned long)tlbmiss_handler_setup_pgd,
237662306a36Sopenharmony_ci			   (unsigned long)tlbmiss_handler_setup_pgd_end);
237762306a36Sopenharmony_ci}
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_cistatic void print_htw_config(void)
238062306a36Sopenharmony_ci{
238162306a36Sopenharmony_ci	unsigned long config;
238262306a36Sopenharmony_ci	unsigned int pwctl;
238362306a36Sopenharmony_ci	const int field = 2 * sizeof(unsigned long);
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	config = read_c0_pwfield();
238662306a36Sopenharmony_ci	pr_debug("PWField (0x%0*lx): GDI: 0x%02lx  UDI: 0x%02lx  MDI: 0x%02lx  PTI: 0x%02lx  PTEI: 0x%02lx\n",
238762306a36Sopenharmony_ci		field, config,
238862306a36Sopenharmony_ci		(config & MIPS_PWFIELD_GDI_MASK) >> MIPS_PWFIELD_GDI_SHIFT,
238962306a36Sopenharmony_ci		(config & MIPS_PWFIELD_UDI_MASK) >> MIPS_PWFIELD_UDI_SHIFT,
239062306a36Sopenharmony_ci		(config & MIPS_PWFIELD_MDI_MASK) >> MIPS_PWFIELD_MDI_SHIFT,
239162306a36Sopenharmony_ci		(config & MIPS_PWFIELD_PTI_MASK) >> MIPS_PWFIELD_PTI_SHIFT,
239262306a36Sopenharmony_ci		(config & MIPS_PWFIELD_PTEI_MASK) >> MIPS_PWFIELD_PTEI_SHIFT);
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	config = read_c0_pwsize();
239562306a36Sopenharmony_ci	pr_debug("PWSize  (0x%0*lx): PS: 0x%lx  GDW: 0x%02lx  UDW: 0x%02lx  MDW: 0x%02lx  PTW: 0x%02lx  PTEW: 0x%02lx\n",
239662306a36Sopenharmony_ci		field, config,
239762306a36Sopenharmony_ci		(config & MIPS_PWSIZE_PS_MASK) >> MIPS_PWSIZE_PS_SHIFT,
239862306a36Sopenharmony_ci		(config & MIPS_PWSIZE_GDW_MASK) >> MIPS_PWSIZE_GDW_SHIFT,
239962306a36Sopenharmony_ci		(config & MIPS_PWSIZE_UDW_MASK) >> MIPS_PWSIZE_UDW_SHIFT,
240062306a36Sopenharmony_ci		(config & MIPS_PWSIZE_MDW_MASK) >> MIPS_PWSIZE_MDW_SHIFT,
240162306a36Sopenharmony_ci		(config & MIPS_PWSIZE_PTW_MASK) >> MIPS_PWSIZE_PTW_SHIFT,
240262306a36Sopenharmony_ci		(config & MIPS_PWSIZE_PTEW_MASK) >> MIPS_PWSIZE_PTEW_SHIFT);
240362306a36Sopenharmony_ci
240462306a36Sopenharmony_ci	pwctl = read_c0_pwctl();
240562306a36Sopenharmony_ci	pr_debug("PWCtl   (0x%x): PWEn: 0x%x  XK: 0x%x  XS: 0x%x  XU: 0x%x  DPH: 0x%x  HugePg: 0x%x  Psn: 0x%x\n",
240662306a36Sopenharmony_ci		pwctl,
240762306a36Sopenharmony_ci		(pwctl & MIPS_PWCTL_PWEN_MASK) >> MIPS_PWCTL_PWEN_SHIFT,
240862306a36Sopenharmony_ci		(pwctl & MIPS_PWCTL_XK_MASK) >> MIPS_PWCTL_XK_SHIFT,
240962306a36Sopenharmony_ci		(pwctl & MIPS_PWCTL_XS_MASK) >> MIPS_PWCTL_XS_SHIFT,
241062306a36Sopenharmony_ci		(pwctl & MIPS_PWCTL_XU_MASK) >> MIPS_PWCTL_XU_SHIFT,
241162306a36Sopenharmony_ci		(pwctl & MIPS_PWCTL_DPH_MASK) >> MIPS_PWCTL_DPH_SHIFT,
241262306a36Sopenharmony_ci		(pwctl & MIPS_PWCTL_HUGEPG_MASK) >> MIPS_PWCTL_HUGEPG_SHIFT,
241362306a36Sopenharmony_ci		(pwctl & MIPS_PWCTL_PSN_MASK) >> MIPS_PWCTL_PSN_SHIFT);
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_cistatic void config_htw_params(void)
241762306a36Sopenharmony_ci{
241862306a36Sopenharmony_ci	unsigned long pwfield, pwsize, ptei;
241962306a36Sopenharmony_ci	unsigned int config;
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	/*
242262306a36Sopenharmony_ci	 * We are using 2-level page tables, so we only need to
242362306a36Sopenharmony_ci	 * setup GDW and PTW appropriately. UDW and MDW will remain 0.
242462306a36Sopenharmony_ci	 * The default value of GDI/UDI/MDI/PTI is 0xc. It is illegal to
242562306a36Sopenharmony_ci	 * write values less than 0xc in these fields because the entire
242662306a36Sopenharmony_ci	 * write will be dropped. As a result of which, we must preserve
242762306a36Sopenharmony_ci	 * the original reset values and overwrite only what we really want.
242862306a36Sopenharmony_ci	 */
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	pwfield = read_c0_pwfield();
243162306a36Sopenharmony_ci	/* re-initialize the GDI field */
243262306a36Sopenharmony_ci	pwfield &= ~MIPS_PWFIELD_GDI_MASK;
243362306a36Sopenharmony_ci	pwfield |= PGDIR_SHIFT << MIPS_PWFIELD_GDI_SHIFT;
243462306a36Sopenharmony_ci	/* re-initialize the PTI field including the even/odd bit */
243562306a36Sopenharmony_ci	pwfield &= ~MIPS_PWFIELD_PTI_MASK;
243662306a36Sopenharmony_ci	pwfield |= PAGE_SHIFT << MIPS_PWFIELD_PTI_SHIFT;
243762306a36Sopenharmony_ci	if (CONFIG_PGTABLE_LEVELS >= 3) {
243862306a36Sopenharmony_ci		pwfield &= ~MIPS_PWFIELD_MDI_MASK;
243962306a36Sopenharmony_ci		pwfield |= PMD_SHIFT << MIPS_PWFIELD_MDI_SHIFT;
244062306a36Sopenharmony_ci	}
244162306a36Sopenharmony_ci	/* Set the PTEI right shift */
244262306a36Sopenharmony_ci	ptei = _PAGE_GLOBAL_SHIFT << MIPS_PWFIELD_PTEI_SHIFT;
244362306a36Sopenharmony_ci	pwfield |= ptei;
244462306a36Sopenharmony_ci	write_c0_pwfield(pwfield);
244562306a36Sopenharmony_ci	/* Check whether the PTEI value is supported */
244662306a36Sopenharmony_ci	back_to_back_c0_hazard();
244762306a36Sopenharmony_ci	pwfield = read_c0_pwfield();
244862306a36Sopenharmony_ci	if (((pwfield & MIPS_PWFIELD_PTEI_MASK) << MIPS_PWFIELD_PTEI_SHIFT)
244962306a36Sopenharmony_ci		!= ptei) {
245062306a36Sopenharmony_ci		pr_warn("Unsupported PTEI field value: 0x%lx. HTW will not be enabled",
245162306a36Sopenharmony_ci			ptei);
245262306a36Sopenharmony_ci		/*
245362306a36Sopenharmony_ci		 * Drop option to avoid HTW being enabled via another path
245462306a36Sopenharmony_ci		 * (eg htw_reset())
245562306a36Sopenharmony_ci		 */
245662306a36Sopenharmony_ci		current_cpu_data.options &= ~MIPS_CPU_HTW;
245762306a36Sopenharmony_ci		return;
245862306a36Sopenharmony_ci	}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	pwsize = ilog2(PTRS_PER_PGD) << MIPS_PWSIZE_GDW_SHIFT;
246162306a36Sopenharmony_ci	pwsize |= ilog2(PTRS_PER_PTE) << MIPS_PWSIZE_PTW_SHIFT;
246262306a36Sopenharmony_ci	if (CONFIG_PGTABLE_LEVELS >= 3)
246362306a36Sopenharmony_ci		pwsize |= ilog2(PTRS_PER_PMD) << MIPS_PWSIZE_MDW_SHIFT;
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	/* Set pointer size to size of directory pointers */
246662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_64BIT))
246762306a36Sopenharmony_ci		pwsize |= MIPS_PWSIZE_PS_MASK;
246862306a36Sopenharmony_ci	/* PTEs may be multiple pointers long (e.g. with XPA) */
246962306a36Sopenharmony_ci	pwsize |= ((PTE_T_LOG2 - PGD_T_LOG2) << MIPS_PWSIZE_PTEW_SHIFT)
247062306a36Sopenharmony_ci			& MIPS_PWSIZE_PTEW_MASK;
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_ci	write_c0_pwsize(pwsize);
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	/* Make sure everything is set before we enable the HTW */
247562306a36Sopenharmony_ci	back_to_back_c0_hazard();
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci	/*
247862306a36Sopenharmony_ci	 * Enable HTW (and only for XUSeg on 64-bit), and disable the rest of
247962306a36Sopenharmony_ci	 * the pwctl fields.
248062306a36Sopenharmony_ci	 */
248162306a36Sopenharmony_ci	config = 1 << MIPS_PWCTL_PWEN_SHIFT;
248262306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_64BIT))
248362306a36Sopenharmony_ci		config |= MIPS_PWCTL_XU_MASK;
248462306a36Sopenharmony_ci	write_c0_pwctl(config);
248562306a36Sopenharmony_ci	pr_info("Hardware Page Table Walker enabled\n");
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ci	print_htw_config();
248862306a36Sopenharmony_ci}
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_cistatic void config_xpa_params(void)
249162306a36Sopenharmony_ci{
249262306a36Sopenharmony_ci#ifdef CONFIG_XPA
249362306a36Sopenharmony_ci	unsigned int pagegrain;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	if (mips_xpa_disabled) {
249662306a36Sopenharmony_ci		pr_info("Extended Physical Addressing (XPA) disabled\n");
249762306a36Sopenharmony_ci		return;
249862306a36Sopenharmony_ci	}
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_ci	pagegrain = read_c0_pagegrain();
250162306a36Sopenharmony_ci	write_c0_pagegrain(pagegrain | PG_ELPA);
250262306a36Sopenharmony_ci	back_to_back_c0_hazard();
250362306a36Sopenharmony_ci	pagegrain = read_c0_pagegrain();
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	if (pagegrain & PG_ELPA)
250662306a36Sopenharmony_ci		pr_info("Extended Physical Addressing (XPA) enabled\n");
250762306a36Sopenharmony_ci	else
250862306a36Sopenharmony_ci		panic("Extended Physical Addressing (XPA) disabled");
250962306a36Sopenharmony_ci#endif
251062306a36Sopenharmony_ci}
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_cistatic void check_pabits(void)
251362306a36Sopenharmony_ci{
251462306a36Sopenharmony_ci	unsigned long entry;
251562306a36Sopenharmony_ci	unsigned pabits, fillbits;
251662306a36Sopenharmony_ci
251762306a36Sopenharmony_ci	if (!cpu_has_rixi || _PAGE_NO_EXEC == 0) {
251862306a36Sopenharmony_ci		/*
251962306a36Sopenharmony_ci		 * We'll only be making use of the fact that we can rotate bits
252062306a36Sopenharmony_ci		 * into the fill if the CPU supports RIXI, so don't bother
252162306a36Sopenharmony_ci		 * probing this for CPUs which don't.
252262306a36Sopenharmony_ci		 */
252362306a36Sopenharmony_ci		return;
252462306a36Sopenharmony_ci	}
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	write_c0_entrylo0(~0ul);
252762306a36Sopenharmony_ci	back_to_back_c0_hazard();
252862306a36Sopenharmony_ci	entry = read_c0_entrylo0();
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_ci	/* clear all non-PFN bits */
253162306a36Sopenharmony_ci	entry &= ~((1 << MIPS_ENTRYLO_PFN_SHIFT) - 1);
253262306a36Sopenharmony_ci	entry &= ~(MIPS_ENTRYLO_RI | MIPS_ENTRYLO_XI);
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	/* find a lower bound on PABITS, and upper bound on fill bits */
253562306a36Sopenharmony_ci	pabits = fls_long(entry) + 6;
253662306a36Sopenharmony_ci	fillbits = max_t(int, (int)BITS_PER_LONG - pabits, 0);
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	/* minus the RI & XI bits */
253962306a36Sopenharmony_ci	fillbits -= min_t(unsigned, fillbits, 2);
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci	if (fillbits >= ilog2(_PAGE_NO_EXEC))
254262306a36Sopenharmony_ci		fill_includes_sw_bits = true;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	pr_debug("Entry* registers contain %u fill bits\n", fillbits);
254562306a36Sopenharmony_ci}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_civoid build_tlb_refill_handler(void)
254862306a36Sopenharmony_ci{
254962306a36Sopenharmony_ci	/*
255062306a36Sopenharmony_ci	 * The refill handler is generated per-CPU, multi-node systems
255162306a36Sopenharmony_ci	 * may have local storage for it. The other handlers are only
255262306a36Sopenharmony_ci	 * needed once.
255362306a36Sopenharmony_ci	 */
255462306a36Sopenharmony_ci	static int run_once = 0;
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_XPA) && !cpu_has_rixi)
255762306a36Sopenharmony_ci		panic("Kernels supporting XPA currently require CPUs with RIXI");
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	output_pgtable_bits_defines();
256062306a36Sopenharmony_ci	check_pabits();
256162306a36Sopenharmony_ci
256262306a36Sopenharmony_ci#ifdef CONFIG_64BIT
256362306a36Sopenharmony_ci	check_for_high_segbits = current_cpu_data.vmbits > (PGDIR_SHIFT + PGD_TABLE_ORDER + PAGE_SHIFT - 3);
256462306a36Sopenharmony_ci#endif
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	if (cpu_has_3kex) {
256762306a36Sopenharmony_ci#ifndef CONFIG_MIPS_PGD_C0_CONTEXT
256862306a36Sopenharmony_ci		if (!run_once) {
256962306a36Sopenharmony_ci			build_setup_pgd();
257062306a36Sopenharmony_ci			build_r3000_tlb_refill_handler();
257162306a36Sopenharmony_ci			build_r3000_tlb_load_handler();
257262306a36Sopenharmony_ci			build_r3000_tlb_store_handler();
257362306a36Sopenharmony_ci			build_r3000_tlb_modify_handler();
257462306a36Sopenharmony_ci			flush_tlb_handlers();
257562306a36Sopenharmony_ci			run_once++;
257662306a36Sopenharmony_ci		}
257762306a36Sopenharmony_ci#else
257862306a36Sopenharmony_ci		panic("No R3000 TLB refill handler");
257962306a36Sopenharmony_ci#endif
258062306a36Sopenharmony_ci		return;
258162306a36Sopenharmony_ci	}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	if (cpu_has_ldpte)
258462306a36Sopenharmony_ci		setup_pw();
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_ci	if (!run_once) {
258762306a36Sopenharmony_ci		scratch_reg = allocate_kscratch();
258862306a36Sopenharmony_ci		build_setup_pgd();
258962306a36Sopenharmony_ci		build_r4000_tlb_load_handler();
259062306a36Sopenharmony_ci		build_r4000_tlb_store_handler();
259162306a36Sopenharmony_ci		build_r4000_tlb_modify_handler();
259262306a36Sopenharmony_ci		if (cpu_has_ldpte)
259362306a36Sopenharmony_ci			build_loongson3_tlb_refill_handler();
259462306a36Sopenharmony_ci		else
259562306a36Sopenharmony_ci			build_r4000_tlb_refill_handler();
259662306a36Sopenharmony_ci		flush_tlb_handlers();
259762306a36Sopenharmony_ci		run_once++;
259862306a36Sopenharmony_ci	}
259962306a36Sopenharmony_ci	if (cpu_has_xpa)
260062306a36Sopenharmony_ci		config_xpa_params();
260162306a36Sopenharmony_ci	if (cpu_has_htw)
260262306a36Sopenharmony_ci		config_htw_params();
260362306a36Sopenharmony_ci}
2604