162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2001 Ben. Herrenschmidt (benh@kernel.crashing.org)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Modifications for ppc64:
662306a36Sopenharmony_ci *      Copyright (C) 2003 Dave Engebretsen <engebret@us.ibm.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci *  Copyright 2008 Michael Ellerman, IBM Corporation.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/types.h>
1262306a36Sopenharmony_ci#include <linux/jump_label.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/string.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci#include <linux/sched/mm.h>
1762306a36Sopenharmony_ci#include <linux/stop_machine.h>
1862306a36Sopenharmony_ci#include <asm/cputable.h>
1962306a36Sopenharmony_ci#include <asm/code-patching.h>
2062306a36Sopenharmony_ci#include <asm/interrupt.h>
2162306a36Sopenharmony_ci#include <asm/page.h>
2262306a36Sopenharmony_ci#include <asm/sections.h>
2362306a36Sopenharmony_ci#include <asm/setup.h>
2462306a36Sopenharmony_ci#include <asm/security_features.h>
2562306a36Sopenharmony_ci#include <asm/firmware.h>
2662306a36Sopenharmony_ci#include <asm/inst.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct fixup_entry {
2962306a36Sopenharmony_ci	unsigned long	mask;
3062306a36Sopenharmony_ci	unsigned long	value;
3162306a36Sopenharmony_ci	long		start_off;
3262306a36Sopenharmony_ci	long		end_off;
3362306a36Sopenharmony_ci	long		alt_start_off;
3462306a36Sopenharmony_ci	long		alt_end_off;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistatic u32 *calc_addr(struct fixup_entry *fcur, long offset)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	/*
4062306a36Sopenharmony_ci	 * We store the offset to the code as a negative offset from
4162306a36Sopenharmony_ci	 * the start of the alt_entry, to support the VDSO. This
4262306a36Sopenharmony_ci	 * routine converts that back into an actual address.
4362306a36Sopenharmony_ci	 */
4462306a36Sopenharmony_ci	return (u32 *)((unsigned long)fcur + offset);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int patch_alt_instruction(u32 *src, u32 *dest, u32 *alt_start, u32 *alt_end)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	int err;
5062306a36Sopenharmony_ci	ppc_inst_t instr;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	instr = ppc_inst_read(src);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (instr_is_relative_branch(ppc_inst_read(src))) {
5562306a36Sopenharmony_ci		u32 *target = (u32 *)branch_target(src);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		/* Branch within the section doesn't need translating */
5862306a36Sopenharmony_ci		if (target < alt_start || target > alt_end) {
5962306a36Sopenharmony_ci			err = translate_branch(&instr, dest, src);
6062306a36Sopenharmony_ci			if (err)
6162306a36Sopenharmony_ci				return 1;
6262306a36Sopenharmony_ci		}
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	raw_patch_instruction(dest, instr);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic int patch_feature_section_mask(unsigned long value, unsigned long mask,
7162306a36Sopenharmony_ci				      struct fixup_entry *fcur)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	u32 *start, *end, *alt_start, *alt_end, *src, *dest;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	start = calc_addr(fcur, fcur->start_off);
7662306a36Sopenharmony_ci	end = calc_addr(fcur, fcur->end_off);
7762306a36Sopenharmony_ci	alt_start = calc_addr(fcur, fcur->alt_start_off);
7862306a36Sopenharmony_ci	alt_end = calc_addr(fcur, fcur->alt_end_off);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if ((alt_end - alt_start) > (end - start))
8162306a36Sopenharmony_ci		return 1;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if ((value & fcur->mask & mask) == (fcur->value & mask))
8462306a36Sopenharmony_ci		return 0;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	src = alt_start;
8762306a36Sopenharmony_ci	dest = start;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	for (; src < alt_end; src = ppc_inst_next(src, src),
9062306a36Sopenharmony_ci			      dest = ppc_inst_next(dest, dest)) {
9162306a36Sopenharmony_ci		if (patch_alt_instruction(src, dest, alt_start, alt_end))
9262306a36Sopenharmony_ci			return 1;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	for (; dest < end; dest++)
9662306a36Sopenharmony_ci		raw_patch_instruction(dest, ppc_inst(PPC_RAW_NOP()));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void do_feature_fixups_mask(unsigned long value, unsigned long mask,
10262306a36Sopenharmony_ci				   void *fixup_start, void *fixup_end)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct fixup_entry *fcur, *fend;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	fcur = fixup_start;
10762306a36Sopenharmony_ci	fend = fixup_end;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	for (; fcur < fend; fcur++) {
11062306a36Sopenharmony_ci		if (patch_feature_section_mask(value, mask, fcur)) {
11162306a36Sopenharmony_ci			WARN_ON(1);
11262306a36Sopenharmony_ci			printk("Unable to patch feature section at %p - %p" \
11362306a36Sopenharmony_ci				" with %p - %p\n",
11462306a36Sopenharmony_ci				calc_addr(fcur, fcur->start_off),
11562306a36Sopenharmony_ci				calc_addr(fcur, fcur->end_off),
11662306a36Sopenharmony_ci				calc_addr(fcur, fcur->alt_start_off),
11762306a36Sopenharmony_ci				calc_addr(fcur, fcur->alt_end_off));
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_civoid do_feature_fixups(unsigned long value, void *fixup_start, void *fixup_end)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	do_feature_fixups_mask(value, ~0, fixup_start, fixup_end);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#ifdef CONFIG_PPC_BARRIER_NOSPEC
12862306a36Sopenharmony_cistatic bool is_fixup_addr_valid(void *dest, size_t size)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	return system_state < SYSTEM_FREEING_INITMEM ||
13162306a36Sopenharmony_ci	       !init_section_contains(dest, size);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int do_patch_fixups(long *start, long *end, unsigned int *instrs, int num)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	int i;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	for (i = 0; start < end; start++, i++) {
13962306a36Sopenharmony_ci		int j;
14062306a36Sopenharmony_ci		unsigned int *dest = (void *)start + *start;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		if (!is_fixup_addr_valid(dest, sizeof(*instrs) * num))
14362306a36Sopenharmony_ci			continue;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		pr_devel("patching dest %lx\n", (unsigned long)dest);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		for (j = 0; j < num; j++)
14862306a36Sopenharmony_ci			patch_instruction(dest + j, ppc_inst(instrs[j]));
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	return i;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci#endif
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#ifdef CONFIG_PPC_BOOK3S_64
15562306a36Sopenharmony_cistatic int do_patch_entry_fixups(long *start, long *end, unsigned int *instrs,
15662306a36Sopenharmony_ci				 bool do_fallback, void *fallback)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	int i;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	for (i = 0; start < end; start++, i++) {
16162306a36Sopenharmony_ci		unsigned int *dest = (void *)start + *start;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci		if (!is_fixup_addr_valid(dest, sizeof(*instrs) * 3))
16462306a36Sopenharmony_ci			continue;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		pr_devel("patching dest %lx\n", (unsigned long)dest);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci		// See comment in do_entry_flush_fixups() RE order of patching
16962306a36Sopenharmony_ci		if (do_fallback) {
17062306a36Sopenharmony_ci			patch_instruction(dest, ppc_inst(instrs[0]));
17162306a36Sopenharmony_ci			patch_instruction(dest + 2, ppc_inst(instrs[2]));
17262306a36Sopenharmony_ci			patch_branch(dest + 1, (unsigned long)fallback, BRANCH_SET_LINK);
17362306a36Sopenharmony_ci		} else {
17462306a36Sopenharmony_ci			patch_instruction(dest + 1, ppc_inst(instrs[1]));
17562306a36Sopenharmony_ci			patch_instruction(dest + 2, ppc_inst(instrs[2]));
17662306a36Sopenharmony_ci			patch_instruction(dest, ppc_inst(instrs[0]));
17762306a36Sopenharmony_ci		}
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci	return i;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void do_stf_entry_barrier_fixups(enum stf_barrier_type types)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	unsigned int instrs[3];
18562306a36Sopenharmony_ci	long *start, *end;
18662306a36Sopenharmony_ci	int i;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	start = PTRRELOC(&__start___stf_entry_barrier_fixup);
18962306a36Sopenharmony_ci	end = PTRRELOC(&__stop___stf_entry_barrier_fixup);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	instrs[0] = PPC_RAW_NOP();
19262306a36Sopenharmony_ci	instrs[1] = PPC_RAW_NOP();
19362306a36Sopenharmony_ci	instrs[2] = PPC_RAW_NOP();
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	i = 0;
19662306a36Sopenharmony_ci	if (types & STF_BARRIER_FALLBACK) {
19762306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_MFLR(_R10);
19862306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_NOP(); /* branch patched below */
19962306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_MTLR(_R10);
20062306a36Sopenharmony_ci	} else if (types & STF_BARRIER_EIEIO) {
20162306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_EIEIO() | 0x02000000; /* eieio + bit 6 hint */
20262306a36Sopenharmony_ci	} else if (types & STF_BARRIER_SYNC_ORI) {
20362306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_SYNC();
20462306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_LD(_R10, _R13, 0);
20562306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	i = do_patch_entry_fixups(start, end, instrs, types & STF_BARRIER_FALLBACK,
20962306a36Sopenharmony_ci				  &stf_barrier_fallback);
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	printk(KERN_DEBUG "stf-barrier: patched %d entry locations (%s barrier)\n", i,
21262306a36Sopenharmony_ci		(types == STF_BARRIER_NONE)                  ? "no" :
21362306a36Sopenharmony_ci		(types == STF_BARRIER_FALLBACK)              ? "fallback" :
21462306a36Sopenharmony_ci		(types == STF_BARRIER_EIEIO)                 ? "eieio" :
21562306a36Sopenharmony_ci		(types == (STF_BARRIER_SYNC_ORI))            ? "hwsync"
21662306a36Sopenharmony_ci		                                           : "unknown");
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void do_stf_exit_barrier_fixups(enum stf_barrier_type types)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	unsigned int instrs[6];
22262306a36Sopenharmony_ci	long *start, *end;
22362306a36Sopenharmony_ci	int i;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	start = PTRRELOC(&__start___stf_exit_barrier_fixup);
22662306a36Sopenharmony_ci	end = PTRRELOC(&__stop___stf_exit_barrier_fixup);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	instrs[0] = PPC_RAW_NOP();
22962306a36Sopenharmony_ci	instrs[1] = PPC_RAW_NOP();
23062306a36Sopenharmony_ci	instrs[2] = PPC_RAW_NOP();
23162306a36Sopenharmony_ci	instrs[3] = PPC_RAW_NOP();
23262306a36Sopenharmony_ci	instrs[4] = PPC_RAW_NOP();
23362306a36Sopenharmony_ci	instrs[5] = PPC_RAW_NOP();
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	i = 0;
23662306a36Sopenharmony_ci	if (types & STF_BARRIER_FALLBACK || types & STF_BARRIER_SYNC_ORI) {
23762306a36Sopenharmony_ci		if (cpu_has_feature(CPU_FTR_HVMODE)) {
23862306a36Sopenharmony_ci			instrs[i++] = PPC_RAW_MTSPR(SPRN_HSPRG1, _R13);
23962306a36Sopenharmony_ci			instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_HSPRG0);
24062306a36Sopenharmony_ci		} else {
24162306a36Sopenharmony_ci			instrs[i++] = PPC_RAW_MTSPR(SPRN_SPRG2, _R13);
24262306a36Sopenharmony_ci			instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_SPRG1);
24362306a36Sopenharmony_ci	        }
24462306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_SYNC();
24562306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_LD(_R13, _R13, 0);
24662306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */
24762306a36Sopenharmony_ci		if (cpu_has_feature(CPU_FTR_HVMODE))
24862306a36Sopenharmony_ci			instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_HSPRG1);
24962306a36Sopenharmony_ci		else
25062306a36Sopenharmony_ci			instrs[i++] = PPC_RAW_MFSPR(_R13, SPRN_SPRG2);
25162306a36Sopenharmony_ci	} else if (types & STF_BARRIER_EIEIO) {
25262306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_EIEIO() | 0x02000000; /* eieio + bit 6 hint */
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs));
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	printk(KERN_DEBUG "stf-barrier: patched %d exit locations (%s barrier)\n", i,
25862306a36Sopenharmony_ci		(types == STF_BARRIER_NONE)                  ? "no" :
25962306a36Sopenharmony_ci		(types == STF_BARRIER_FALLBACK)              ? "fallback" :
26062306a36Sopenharmony_ci		(types == STF_BARRIER_EIEIO)                 ? "eieio" :
26162306a36Sopenharmony_ci		(types == (STF_BARRIER_SYNC_ORI))            ? "hwsync"
26262306a36Sopenharmony_ci		                                           : "unknown");
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic bool stf_exit_reentrant = false;
26662306a36Sopenharmony_cistatic bool rfi_exit_reentrant = false;
26762306a36Sopenharmony_cistatic DEFINE_MUTEX(exit_flush_lock);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic int __do_stf_barrier_fixups(void *data)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	enum stf_barrier_type *types = data;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	do_stf_entry_barrier_fixups(*types);
27462306a36Sopenharmony_ci	do_stf_exit_barrier_fixups(*types);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	return 0;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_civoid do_stf_barrier_fixups(enum stf_barrier_type types)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	/*
28262306a36Sopenharmony_ci	 * The call to the fallback entry flush, and the fallback/sync-ori exit
28362306a36Sopenharmony_ci	 * flush can not be safely patched in/out while other CPUs are
28462306a36Sopenharmony_ci	 * executing them. So call __do_stf_barrier_fixups() on one CPU while
28562306a36Sopenharmony_ci	 * all other CPUs spin in the stop machine core with interrupts hard
28662306a36Sopenharmony_ci	 * disabled.
28762306a36Sopenharmony_ci	 *
28862306a36Sopenharmony_ci	 * The branch to mark interrupt exits non-reentrant is enabled first,
28962306a36Sopenharmony_ci	 * then stop_machine runs which will ensure all CPUs are out of the
29062306a36Sopenharmony_ci	 * low level interrupt exit code before patching. After the patching,
29162306a36Sopenharmony_ci	 * if allowed, then flip the branch to allow fast exits.
29262306a36Sopenharmony_ci	 */
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	// Prevent static key update races with do_rfi_flush_fixups()
29562306a36Sopenharmony_ci	mutex_lock(&exit_flush_lock);
29662306a36Sopenharmony_ci	static_branch_enable(&interrupt_exit_not_reentrant);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	stop_machine(__do_stf_barrier_fixups, &types, NULL);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	if ((types & STF_BARRIER_FALLBACK) || (types & STF_BARRIER_SYNC_ORI))
30162306a36Sopenharmony_ci		stf_exit_reentrant = false;
30262306a36Sopenharmony_ci	else
30362306a36Sopenharmony_ci		stf_exit_reentrant = true;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (stf_exit_reentrant && rfi_exit_reentrant)
30662306a36Sopenharmony_ci		static_branch_disable(&interrupt_exit_not_reentrant);
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	mutex_unlock(&exit_flush_lock);
30962306a36Sopenharmony_ci}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_civoid do_uaccess_flush_fixups(enum l1d_flush_type types)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	unsigned int instrs[4];
31462306a36Sopenharmony_ci	long *start, *end;
31562306a36Sopenharmony_ci	int i;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	start = PTRRELOC(&__start___uaccess_flush_fixup);
31862306a36Sopenharmony_ci	end = PTRRELOC(&__stop___uaccess_flush_fixup);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	instrs[0] = PPC_RAW_NOP();
32162306a36Sopenharmony_ci	instrs[1] = PPC_RAW_NOP();
32262306a36Sopenharmony_ci	instrs[2] = PPC_RAW_NOP();
32362306a36Sopenharmony_ci	instrs[3] = PPC_RAW_BLR();
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	i = 0;
32662306a36Sopenharmony_ci	if (types == L1D_FLUSH_FALLBACK) {
32762306a36Sopenharmony_ci		instrs[3] = PPC_RAW_NOP();
32862306a36Sopenharmony_ci		/* fallthrough to fallback flush */
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (types & L1D_FLUSH_ORI) {
33262306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */
33362306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (types & L1D_FLUSH_MTTRIG)
33762306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs));
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	printk(KERN_DEBUG "uaccess-flush: patched %d locations (%s flush)\n", i,
34262306a36Sopenharmony_ci		(types == L1D_FLUSH_NONE)       ? "no" :
34362306a36Sopenharmony_ci		(types == L1D_FLUSH_FALLBACK)   ? "fallback displacement" :
34462306a36Sopenharmony_ci		(types &  L1D_FLUSH_ORI)        ? (types & L1D_FLUSH_MTTRIG)
34562306a36Sopenharmony_ci							? "ori+mttrig type"
34662306a36Sopenharmony_ci							: "ori type" :
34762306a36Sopenharmony_ci		(types &  L1D_FLUSH_MTTRIG)     ? "mttrig type"
34862306a36Sopenharmony_ci						: "unknown");
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int __do_entry_flush_fixups(void *data)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	enum l1d_flush_type types = *(enum l1d_flush_type *)data;
35462306a36Sopenharmony_ci	unsigned int instrs[3];
35562306a36Sopenharmony_ci	long *start, *end;
35662306a36Sopenharmony_ci	int i;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	instrs[0] = PPC_RAW_NOP();
35962306a36Sopenharmony_ci	instrs[1] = PPC_RAW_NOP();
36062306a36Sopenharmony_ci	instrs[2] = PPC_RAW_NOP();
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	i = 0;
36362306a36Sopenharmony_ci	if (types == L1D_FLUSH_FALLBACK) {
36462306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_MFLR(_R10);
36562306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_NOP(); /* branch patched below */
36662306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_MTLR(_R10);
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	if (types & L1D_FLUSH_ORI) {
37062306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */
37162306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (types & L1D_FLUSH_MTTRIG)
37562306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/*
37862306a36Sopenharmony_ci	 * If we're patching in or out the fallback flush we need to be careful about the
37962306a36Sopenharmony_ci	 * order in which we patch instructions. That's because it's possible we could
38062306a36Sopenharmony_ci	 * take a page fault after patching one instruction, so the sequence of
38162306a36Sopenharmony_ci	 * instructions must be safe even in a half patched state.
38262306a36Sopenharmony_ci	 *
38362306a36Sopenharmony_ci	 * To make that work, when patching in the fallback flush we patch in this order:
38462306a36Sopenharmony_ci	 *  - the mflr		(dest)
38562306a36Sopenharmony_ci	 *  - the mtlr		(dest + 2)
38662306a36Sopenharmony_ci	 *  - the branch	(dest + 1)
38762306a36Sopenharmony_ci	 *
38862306a36Sopenharmony_ci	 * That ensures the sequence is safe to execute at any point. In contrast if we
38962306a36Sopenharmony_ci	 * patch the mtlr last, it's possible we could return from the branch and not
39062306a36Sopenharmony_ci	 * restore LR, leading to a crash later.
39162306a36Sopenharmony_ci	 *
39262306a36Sopenharmony_ci	 * When patching out the fallback flush (either with nops or another flush type),
39362306a36Sopenharmony_ci	 * we patch in this order:
39462306a36Sopenharmony_ci	 *  - the branch	(dest + 1)
39562306a36Sopenharmony_ci	 *  - the mtlr		(dest + 2)
39662306a36Sopenharmony_ci	 *  - the mflr		(dest)
39762306a36Sopenharmony_ci	 *
39862306a36Sopenharmony_ci	 * Note we are protected by stop_machine() from other CPUs executing the code in a
39962306a36Sopenharmony_ci	 * semi-patched state.
40062306a36Sopenharmony_ci	 */
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	start = PTRRELOC(&__start___entry_flush_fixup);
40362306a36Sopenharmony_ci	end = PTRRELOC(&__stop___entry_flush_fixup);
40462306a36Sopenharmony_ci	i = do_patch_entry_fixups(start, end, instrs, types == L1D_FLUSH_FALLBACK,
40562306a36Sopenharmony_ci				  &entry_flush_fallback);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	start = PTRRELOC(&__start___scv_entry_flush_fixup);
40862306a36Sopenharmony_ci	end = PTRRELOC(&__stop___scv_entry_flush_fixup);
40962306a36Sopenharmony_ci	i += do_patch_entry_fixups(start, end, instrs, types == L1D_FLUSH_FALLBACK,
41062306a36Sopenharmony_ci				   &scv_entry_flush_fallback);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	printk(KERN_DEBUG "entry-flush: patched %d locations (%s flush)\n", i,
41362306a36Sopenharmony_ci		(types == L1D_FLUSH_NONE)       ? "no" :
41462306a36Sopenharmony_ci		(types == L1D_FLUSH_FALLBACK)   ? "fallback displacement" :
41562306a36Sopenharmony_ci		(types &  L1D_FLUSH_ORI)        ? (types & L1D_FLUSH_MTTRIG)
41662306a36Sopenharmony_ci							? "ori+mttrig type"
41762306a36Sopenharmony_ci							: "ori type" :
41862306a36Sopenharmony_ci		(types &  L1D_FLUSH_MTTRIG)     ? "mttrig type"
41962306a36Sopenharmony_ci						: "unknown");
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_civoid do_entry_flush_fixups(enum l1d_flush_type types)
42562306a36Sopenharmony_ci{
42662306a36Sopenharmony_ci	/*
42762306a36Sopenharmony_ci	 * The call to the fallback flush can not be safely patched in/out while
42862306a36Sopenharmony_ci	 * other CPUs are executing it. So call __do_entry_flush_fixups() on one
42962306a36Sopenharmony_ci	 * CPU while all other CPUs spin in the stop machine core with interrupts
43062306a36Sopenharmony_ci	 * hard disabled.
43162306a36Sopenharmony_ci	 */
43262306a36Sopenharmony_ci	stop_machine(__do_entry_flush_fixups, &types, NULL);
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int __do_rfi_flush_fixups(void *data)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	enum l1d_flush_type types = *(enum l1d_flush_type *)data;
43862306a36Sopenharmony_ci	unsigned int instrs[3];
43962306a36Sopenharmony_ci	long *start, *end;
44062306a36Sopenharmony_ci	int i;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	start = PTRRELOC(&__start___rfi_flush_fixup);
44362306a36Sopenharmony_ci	end = PTRRELOC(&__stop___rfi_flush_fixup);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	instrs[0] = PPC_RAW_NOP();
44662306a36Sopenharmony_ci	instrs[1] = PPC_RAW_NOP();
44762306a36Sopenharmony_ci	instrs[2] = PPC_RAW_NOP();
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (types & L1D_FLUSH_FALLBACK)
45062306a36Sopenharmony_ci		/* b .+16 to fallback flush */
45162306a36Sopenharmony_ci		instrs[0] = PPC_RAW_BRANCH(16);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	i = 0;
45462306a36Sopenharmony_ci	if (types & L1D_FLUSH_ORI) {
45562306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */
45662306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_ORI(_R30, _R30, 0); /* L1d flush */
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (types & L1D_FLUSH_MTTRIG)
46062306a36Sopenharmony_ci		instrs[i++] = PPC_RAW_MTSPR(SPRN_TRIG2, _R0);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	i = do_patch_fixups(start, end, instrs, ARRAY_SIZE(instrs));
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	printk(KERN_DEBUG "rfi-flush: patched %d locations (%s flush)\n", i,
46562306a36Sopenharmony_ci		(types == L1D_FLUSH_NONE)       ? "no" :
46662306a36Sopenharmony_ci		(types == L1D_FLUSH_FALLBACK)   ? "fallback displacement" :
46762306a36Sopenharmony_ci		(types &  L1D_FLUSH_ORI)        ? (types & L1D_FLUSH_MTTRIG)
46862306a36Sopenharmony_ci							? "ori+mttrig type"
46962306a36Sopenharmony_ci							: "ori type" :
47062306a36Sopenharmony_ci		(types &  L1D_FLUSH_MTTRIG)     ? "mttrig type"
47162306a36Sopenharmony_ci						: "unknown");
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return 0;
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_civoid do_rfi_flush_fixups(enum l1d_flush_type types)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	/*
47962306a36Sopenharmony_ci	 * stop_machine gets all CPUs out of the interrupt exit handler same
48062306a36Sopenharmony_ci	 * as do_stf_barrier_fixups. do_rfi_flush_fixups patching can run
48162306a36Sopenharmony_ci	 * without stop_machine, so this could be achieved with a broadcast
48262306a36Sopenharmony_ci	 * IPI instead, but this matches the stf sequence.
48362306a36Sopenharmony_ci	 */
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	// Prevent static key update races with do_stf_barrier_fixups()
48662306a36Sopenharmony_ci	mutex_lock(&exit_flush_lock);
48762306a36Sopenharmony_ci	static_branch_enable(&interrupt_exit_not_reentrant);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	stop_machine(__do_rfi_flush_fixups, &types, NULL);
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	if (types & L1D_FLUSH_FALLBACK)
49262306a36Sopenharmony_ci		rfi_exit_reentrant = false;
49362306a36Sopenharmony_ci	else
49462306a36Sopenharmony_ci		rfi_exit_reentrant = true;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (stf_exit_reentrant && rfi_exit_reentrant)
49762306a36Sopenharmony_ci		static_branch_disable(&interrupt_exit_not_reentrant);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	mutex_unlock(&exit_flush_lock);
50062306a36Sopenharmony_ci}
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_civoid do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	unsigned int instr;
50562306a36Sopenharmony_ci	long *start, *end;
50662306a36Sopenharmony_ci	int i;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	start = fixup_start;
50962306a36Sopenharmony_ci	end = fixup_end;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	instr = PPC_RAW_NOP();
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	if (enable) {
51462306a36Sopenharmony_ci		pr_info("barrier-nospec: using ORI speculation barrier\n");
51562306a36Sopenharmony_ci		instr = PPC_RAW_ORI(_R31, _R31, 0); /* speculation barrier */
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	i = do_patch_fixups(start, end, &instr, 1);
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i);
52162306a36Sopenharmony_ci}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci#endif /* CONFIG_PPC_BOOK3S_64 */
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci#ifdef CONFIG_PPC_BARRIER_NOSPEC
52662306a36Sopenharmony_civoid do_barrier_nospec_fixups(bool enable)
52762306a36Sopenharmony_ci{
52862306a36Sopenharmony_ci	void *start, *end;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	start = PTRRELOC(&__start___barrier_nospec_fixup);
53162306a36Sopenharmony_ci	end = PTRRELOC(&__stop___barrier_nospec_fixup);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	do_barrier_nospec_fixups_range(enable, start, end);
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci#endif /* CONFIG_PPC_BARRIER_NOSPEC */
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci#ifdef CONFIG_PPC_E500
53862306a36Sopenharmony_civoid do_barrier_nospec_fixups_range(bool enable, void *fixup_start, void *fixup_end)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	unsigned int instr[2];
54162306a36Sopenharmony_ci	long *start, *end;
54262306a36Sopenharmony_ci	int i;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	start = fixup_start;
54562306a36Sopenharmony_ci	end = fixup_end;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	instr[0] = PPC_RAW_NOP();
54862306a36Sopenharmony_ci	instr[1] = PPC_RAW_NOP();
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	if (enable) {
55162306a36Sopenharmony_ci		pr_info("barrier-nospec: using isync; sync as speculation barrier\n");
55262306a36Sopenharmony_ci		instr[0] = PPC_RAW_ISYNC();
55362306a36Sopenharmony_ci		instr[1] = PPC_RAW_SYNC();
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	i = do_patch_fixups(start, end, instr, ARRAY_SIZE(instr));
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	printk(KERN_DEBUG "barrier-nospec: patched %d locations\n", i);
55962306a36Sopenharmony_ci}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_cistatic void __init patch_btb_flush_section(long *curr)
56262306a36Sopenharmony_ci{
56362306a36Sopenharmony_ci	unsigned int *start, *end;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	start = (void *)curr + *curr;
56662306a36Sopenharmony_ci	end = (void *)curr + *(curr + 1);
56762306a36Sopenharmony_ci	for (; start < end; start++) {
56862306a36Sopenharmony_ci		pr_devel("patching dest %lx\n", (unsigned long)start);
56962306a36Sopenharmony_ci		patch_instruction(start, ppc_inst(PPC_RAW_NOP()));
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_civoid __init do_btb_flush_fixups(void)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	long *start, *end;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	start = PTRRELOC(&__start__btb_flush_fixup);
57862306a36Sopenharmony_ci	end = PTRRELOC(&__stop__btb_flush_fixup);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	for (; start < end; start += 2)
58162306a36Sopenharmony_ci		patch_btb_flush_section(start);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci#endif /* CONFIG_PPC_E500 */
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_civoid do_lwsync_fixups(unsigned long value, void *fixup_start, void *fixup_end)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	long *start, *end;
58862306a36Sopenharmony_ci	u32 *dest;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (!(value & CPU_FTR_LWSYNC))
59162306a36Sopenharmony_ci		return ;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	start = fixup_start;
59462306a36Sopenharmony_ci	end = fixup_end;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	for (; start < end; start++) {
59762306a36Sopenharmony_ci		dest = (void *)start + *start;
59862306a36Sopenharmony_ci		raw_patch_instruction(dest, ppc_inst(PPC_INST_LWSYNC));
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic void __init do_final_fixups(void)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci#if defined(CONFIG_PPC64) && defined(CONFIG_RELOCATABLE)
60562306a36Sopenharmony_ci	ppc_inst_t inst;
60662306a36Sopenharmony_ci	u32 *src, *dest, *end;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	if (PHYSICAL_START == 0)
60962306a36Sopenharmony_ci		return;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	src = (u32 *)(KERNELBASE + PHYSICAL_START);
61262306a36Sopenharmony_ci	dest = (u32 *)KERNELBASE;
61362306a36Sopenharmony_ci	end = (void *)src + (__end_interrupts - _stext);
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	while (src < end) {
61662306a36Sopenharmony_ci		inst = ppc_inst_read(src);
61762306a36Sopenharmony_ci		raw_patch_instruction(dest, inst);
61862306a36Sopenharmony_ci		src = ppc_inst_next(src, src);
61962306a36Sopenharmony_ci		dest = ppc_inst_next(dest, dest);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci#endif
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_cistatic unsigned long __initdata saved_cpu_features;
62562306a36Sopenharmony_cistatic unsigned int __initdata saved_mmu_features;
62662306a36Sopenharmony_ci#ifdef CONFIG_PPC64
62762306a36Sopenharmony_cistatic unsigned long __initdata saved_firmware_features;
62862306a36Sopenharmony_ci#endif
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_civoid __init apply_feature_fixups(void)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	struct cpu_spec *spec = PTRRELOC(*PTRRELOC(&cur_cpu_spec));
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	*PTRRELOC(&saved_cpu_features) = spec->cpu_features;
63562306a36Sopenharmony_ci	*PTRRELOC(&saved_mmu_features) = spec->mmu_features;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci	/*
63862306a36Sopenharmony_ci	 * Apply the CPU-specific and firmware specific fixups to kernel text
63962306a36Sopenharmony_ci	 * (nop out sections not relevant to this CPU or this firmware).
64062306a36Sopenharmony_ci	 */
64162306a36Sopenharmony_ci	do_feature_fixups(spec->cpu_features,
64262306a36Sopenharmony_ci			  PTRRELOC(&__start___ftr_fixup),
64362306a36Sopenharmony_ci			  PTRRELOC(&__stop___ftr_fixup));
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	do_feature_fixups(spec->mmu_features,
64662306a36Sopenharmony_ci			  PTRRELOC(&__start___mmu_ftr_fixup),
64762306a36Sopenharmony_ci			  PTRRELOC(&__stop___mmu_ftr_fixup));
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	do_lwsync_fixups(spec->cpu_features,
65062306a36Sopenharmony_ci			 PTRRELOC(&__start___lwsync_fixup),
65162306a36Sopenharmony_ci			 PTRRELOC(&__stop___lwsync_fixup));
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci#ifdef CONFIG_PPC64
65462306a36Sopenharmony_ci	saved_firmware_features = powerpc_firmware_features;
65562306a36Sopenharmony_ci	do_feature_fixups(powerpc_firmware_features,
65662306a36Sopenharmony_ci			  &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup);
65762306a36Sopenharmony_ci#endif
65862306a36Sopenharmony_ci	do_final_fixups();
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_civoid __init update_mmu_feature_fixups(unsigned long mask)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	saved_mmu_features &= ~mask;
66462306a36Sopenharmony_ci	saved_mmu_features |= cur_cpu_spec->mmu_features & mask;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	do_feature_fixups_mask(cur_cpu_spec->mmu_features, mask,
66762306a36Sopenharmony_ci			       PTRRELOC(&__start___mmu_ftr_fixup),
66862306a36Sopenharmony_ci			       PTRRELOC(&__stop___mmu_ftr_fixup));
66962306a36Sopenharmony_ci	mmu_feature_keys_init();
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_civoid __init setup_feature_keys(void)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	/*
67562306a36Sopenharmony_ci	 * Initialise jump label. This causes all the cpu/mmu_has_feature()
67662306a36Sopenharmony_ci	 * checks to take on their correct polarity based on the current set of
67762306a36Sopenharmony_ci	 * CPU/MMU features.
67862306a36Sopenharmony_ci	 */
67962306a36Sopenharmony_ci	jump_label_init();
68062306a36Sopenharmony_ci	cpu_feature_keys_init();
68162306a36Sopenharmony_ci	mmu_feature_keys_init();
68262306a36Sopenharmony_ci}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int __init check_features(void)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	WARN(saved_cpu_features != cur_cpu_spec->cpu_features,
68762306a36Sopenharmony_ci	     "CPU features changed after feature patching!\n");
68862306a36Sopenharmony_ci	WARN(saved_mmu_features != cur_cpu_spec->mmu_features,
68962306a36Sopenharmony_ci	     "MMU features changed after feature patching!\n");
69062306a36Sopenharmony_ci#ifdef CONFIG_PPC64
69162306a36Sopenharmony_ci	WARN(saved_firmware_features != powerpc_firmware_features,
69262306a36Sopenharmony_ci	     "Firmware features changed after feature patching!\n");
69362306a36Sopenharmony_ci#endif
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_cilate_initcall(check_features);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci#ifdef CONFIG_FTR_FIXUP_SELFTEST
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci#define check(x)	\
70262306a36Sopenharmony_ci	if (!(x)) printk("feature-fixups: test failed at line %d\n", __LINE__);
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int patch_feature_section(unsigned long value, struct fixup_entry *fcur)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	return patch_feature_section_mask(value, ~0, fcur);
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci/* This must be after the text it fixes up, vmlinux.lds.S enforces that atm */
71062306a36Sopenharmony_cistatic struct fixup_entry fixup;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic long __init calc_offset(struct fixup_entry *entry, unsigned int *p)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	return (unsigned long)p - (unsigned long)entry;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_cistatic void __init test_basic_patching(void)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test1[];
72062306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_test1[];
72162306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test1_orig[];
72262306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test1_expected[];
72362306a36Sopenharmony_ci	int size = 4 * (end_ftr_fixup_test1 - ftr_fixup_test1);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	fixup.value = fixup.mask = 8;
72662306a36Sopenharmony_ci	fixup.start_off = calc_offset(&fixup, ftr_fixup_test1 + 1);
72762306a36Sopenharmony_ci	fixup.end_off = calc_offset(&fixup, ftr_fixup_test1 + 2);
72862306a36Sopenharmony_ci	fixup.alt_start_off = fixup.alt_end_off = 0;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	/* Sanity check */
73162306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	/* Check we don't patch if the value matches */
73462306a36Sopenharmony_ci	patch_feature_section(8, &fixup);
73562306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* Check we do patch if the value doesn't match */
73862306a36Sopenharmony_ci	patch_feature_section(0, &fixup);
73962306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	/* Check we do patch if the mask doesn't match */
74262306a36Sopenharmony_ci	memcpy(ftr_fixup_test1, ftr_fixup_test1_orig, size);
74362306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_orig, size) == 0);
74462306a36Sopenharmony_ci	patch_feature_section(~8, &fixup);
74562306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test1, ftr_fixup_test1_expected, size) == 0);
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic void __init test_alternative_patching(void)
74962306a36Sopenharmony_ci{
75062306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test2[];
75162306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_test2[];
75262306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test2_orig[];
75362306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test2_alt[];
75462306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test2_expected[];
75562306a36Sopenharmony_ci	int size = 4 * (end_ftr_fixup_test2 - ftr_fixup_test2);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	fixup.value = fixup.mask = 0xF;
75862306a36Sopenharmony_ci	fixup.start_off = calc_offset(&fixup, ftr_fixup_test2 + 1);
75962306a36Sopenharmony_ci	fixup.end_off = calc_offset(&fixup, ftr_fixup_test2 + 2);
76062306a36Sopenharmony_ci	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test2_alt);
76162306a36Sopenharmony_ci	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test2_alt + 1);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	/* Sanity check */
76462306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	/* Check we don't patch if the value matches */
76762306a36Sopenharmony_ci	patch_feature_section(0xF, &fixup);
76862306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	/* Check we do patch if the value doesn't match */
77162306a36Sopenharmony_ci	patch_feature_section(0, &fixup);
77262306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	/* Check we do patch if the mask doesn't match */
77562306a36Sopenharmony_ci	memcpy(ftr_fixup_test2, ftr_fixup_test2_orig, size);
77662306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_orig, size) == 0);
77762306a36Sopenharmony_ci	patch_feature_section(~0xF, &fixup);
77862306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test2, ftr_fixup_test2_expected, size) == 0);
77962306a36Sopenharmony_ci}
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_cistatic void __init test_alternative_case_too_big(void)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test3[];
78462306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_test3[];
78562306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test3_orig[];
78662306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test3_alt[];
78762306a36Sopenharmony_ci	int size = 4 * (end_ftr_fixup_test3 - ftr_fixup_test3);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	fixup.value = fixup.mask = 0xC;
79062306a36Sopenharmony_ci	fixup.start_off = calc_offset(&fixup, ftr_fixup_test3 + 1);
79162306a36Sopenharmony_ci	fixup.end_off = calc_offset(&fixup, ftr_fixup_test3 + 2);
79262306a36Sopenharmony_ci	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test3_alt);
79362306a36Sopenharmony_ci	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test3_alt + 2);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	/* Sanity check */
79662306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	/* Expect nothing to be patched, and the error returned to us */
79962306a36Sopenharmony_ci	check(patch_feature_section(0xF, &fixup) == 1);
80062306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
80162306a36Sopenharmony_ci	check(patch_feature_section(0, &fixup) == 1);
80262306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
80362306a36Sopenharmony_ci	check(patch_feature_section(~0xF, &fixup) == 1);
80462306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test3, ftr_fixup_test3_orig, size) == 0);
80562306a36Sopenharmony_ci}
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_cistatic void __init test_alternative_case_too_small(void)
80862306a36Sopenharmony_ci{
80962306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test4[];
81062306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_test4[];
81162306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test4_orig[];
81262306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test4_alt[];
81362306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test4_expected[];
81462306a36Sopenharmony_ci	int size = 4 * (end_ftr_fixup_test4 - ftr_fixup_test4);
81562306a36Sopenharmony_ci	unsigned long flag;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	/* Check a high-bit flag */
81862306a36Sopenharmony_ci	flag = 1UL << ((sizeof(unsigned long) - 1) * 8);
81962306a36Sopenharmony_ci	fixup.value = fixup.mask = flag;
82062306a36Sopenharmony_ci	fixup.start_off = calc_offset(&fixup, ftr_fixup_test4 + 1);
82162306a36Sopenharmony_ci	fixup.end_off = calc_offset(&fixup, ftr_fixup_test4 + 5);
82262306a36Sopenharmony_ci	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_test4_alt);
82362306a36Sopenharmony_ci	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_test4_alt + 2);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	/* Sanity check */
82662306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	/* Check we don't patch if the value matches */
82962306a36Sopenharmony_ci	patch_feature_section(flag, &fixup);
83062306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	/* Check we do patch if the value doesn't match */
83362306a36Sopenharmony_ci	patch_feature_section(0, &fixup);
83462306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	/* Check we do patch if the mask doesn't match */
83762306a36Sopenharmony_ci	memcpy(ftr_fixup_test4, ftr_fixup_test4_orig, size);
83862306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_orig, size) == 0);
83962306a36Sopenharmony_ci	patch_feature_section(~flag, &fixup);
84062306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test4, ftr_fixup_test4_expected, size) == 0);
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic void test_alternative_case_with_branch(void)
84462306a36Sopenharmony_ci{
84562306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test5[];
84662306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_test5[];
84762306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test5_expected[];
84862306a36Sopenharmony_ci	int size = 4 * (end_ftr_fixup_test5 - ftr_fixup_test5);
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test5, ftr_fixup_test5_expected, size) == 0);
85162306a36Sopenharmony_ci}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_cistatic void __init test_alternative_case_with_external_branch(void)
85462306a36Sopenharmony_ci{
85562306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test6[];
85662306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_test6[];
85762306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test6_expected[];
85862306a36Sopenharmony_ci	int size = 4 * (end_ftr_fixup_test6 - ftr_fixup_test6);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test6, ftr_fixup_test6_expected, size) == 0);
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_cistatic void __init test_alternative_case_with_branch_to_end(void)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test7[];
86662306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_test7[];
86762306a36Sopenharmony_ci	extern unsigned int ftr_fixup_test7_expected[];
86862306a36Sopenharmony_ci	int size = 4 * (end_ftr_fixup_test7 - ftr_fixup_test7);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test7, ftr_fixup_test7_expected, size) == 0);
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic void __init test_cpu_macros(void)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	extern u8 ftr_fixup_test_FTR_macros[];
87662306a36Sopenharmony_ci	extern u8 ftr_fixup_test_FTR_macros_expected[];
87762306a36Sopenharmony_ci	unsigned long size = ftr_fixup_test_FTR_macros_expected -
87862306a36Sopenharmony_ci			     ftr_fixup_test_FTR_macros;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	/* The fixups have already been done for us during boot */
88162306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test_FTR_macros,
88262306a36Sopenharmony_ci		     ftr_fixup_test_FTR_macros_expected, size) == 0);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic void __init test_fw_macros(void)
88662306a36Sopenharmony_ci{
88762306a36Sopenharmony_ci#ifdef CONFIG_PPC64
88862306a36Sopenharmony_ci	extern u8 ftr_fixup_test_FW_FTR_macros[];
88962306a36Sopenharmony_ci	extern u8 ftr_fixup_test_FW_FTR_macros_expected[];
89062306a36Sopenharmony_ci	unsigned long size = ftr_fixup_test_FW_FTR_macros_expected -
89162306a36Sopenharmony_ci			     ftr_fixup_test_FW_FTR_macros;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	/* The fixups have already been done for us during boot */
89462306a36Sopenharmony_ci	check(memcmp(ftr_fixup_test_FW_FTR_macros,
89562306a36Sopenharmony_ci		     ftr_fixup_test_FW_FTR_macros_expected, size) == 0);
89662306a36Sopenharmony_ci#endif
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_cistatic void __init test_lwsync_macros(void)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	extern u8 lwsync_fixup_test[];
90262306a36Sopenharmony_ci	extern u8 end_lwsync_fixup_test[];
90362306a36Sopenharmony_ci	extern u8 lwsync_fixup_test_expected_LWSYNC[];
90462306a36Sopenharmony_ci	extern u8 lwsync_fixup_test_expected_SYNC[];
90562306a36Sopenharmony_ci	unsigned long size = end_lwsync_fixup_test -
90662306a36Sopenharmony_ci			     lwsync_fixup_test;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	/* The fixups have already been done for us during boot */
90962306a36Sopenharmony_ci	if (cur_cpu_spec->cpu_features & CPU_FTR_LWSYNC) {
91062306a36Sopenharmony_ci		check(memcmp(lwsync_fixup_test,
91162306a36Sopenharmony_ci			     lwsync_fixup_test_expected_LWSYNC, size) == 0);
91262306a36Sopenharmony_ci	} else {
91362306a36Sopenharmony_ci		check(memcmp(lwsync_fixup_test,
91462306a36Sopenharmony_ci			     lwsync_fixup_test_expected_SYNC, size) == 0);
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci}
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci#ifdef CONFIG_PPC64
91962306a36Sopenharmony_cistatic void __init test_prefix_patching(void)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix1[];
92262306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_prefix1[];
92362306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix1_orig[];
92462306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix1_expected[];
92562306a36Sopenharmony_ci	int size = sizeof(unsigned int) * (end_ftr_fixup_prefix1 - ftr_fixup_prefix1);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	fixup.value = fixup.mask = 8;
92862306a36Sopenharmony_ci	fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix1 + 1);
92962306a36Sopenharmony_ci	fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix1 + 3);
93062306a36Sopenharmony_ci	fixup.alt_start_off = fixup.alt_end_off = 0;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	/* Sanity check */
93362306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) == 0);
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	patch_feature_section(0, &fixup);
93662306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_expected, size) == 0);
93762306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix1, ftr_fixup_prefix1_orig, size) != 0);
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic void __init test_prefix_alt_patching(void)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix2[];
94362306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_prefix2[];
94462306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix2_orig[];
94562306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix2_expected[];
94662306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix2_alt[];
94762306a36Sopenharmony_ci	int size = sizeof(unsigned int) * (end_ftr_fixup_prefix2 - ftr_fixup_prefix2);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	fixup.value = fixup.mask = 8;
95062306a36Sopenharmony_ci	fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix2 + 1);
95162306a36Sopenharmony_ci	fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix2 + 3);
95262306a36Sopenharmony_ci	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix2_alt);
95362306a36Sopenharmony_ci	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix2_alt + 2);
95462306a36Sopenharmony_ci	/* Sanity check */
95562306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) == 0);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	patch_feature_section(0, &fixup);
95862306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_expected, size) == 0);
95962306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix2, ftr_fixup_prefix2_orig, size) != 0);
96062306a36Sopenharmony_ci}
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_cistatic void __init test_prefix_word_alt_patching(void)
96362306a36Sopenharmony_ci{
96462306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix3[];
96562306a36Sopenharmony_ci	extern unsigned int end_ftr_fixup_prefix3[];
96662306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix3_orig[];
96762306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix3_expected[];
96862306a36Sopenharmony_ci	extern unsigned int ftr_fixup_prefix3_alt[];
96962306a36Sopenharmony_ci	int size = sizeof(unsigned int) * (end_ftr_fixup_prefix3 - ftr_fixup_prefix3);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	fixup.value = fixup.mask = 8;
97262306a36Sopenharmony_ci	fixup.start_off = calc_offset(&fixup, ftr_fixup_prefix3 + 1);
97362306a36Sopenharmony_ci	fixup.end_off = calc_offset(&fixup, ftr_fixup_prefix3 + 4);
97462306a36Sopenharmony_ci	fixup.alt_start_off = calc_offset(&fixup, ftr_fixup_prefix3_alt);
97562306a36Sopenharmony_ci	fixup.alt_end_off = calc_offset(&fixup, ftr_fixup_prefix3_alt + 3);
97662306a36Sopenharmony_ci	/* Sanity check */
97762306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) == 0);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	patch_feature_section(0, &fixup);
98062306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_expected, size) == 0);
98162306a36Sopenharmony_ci	patch_feature_section(0, &fixup);
98262306a36Sopenharmony_ci	check(memcmp(ftr_fixup_prefix3, ftr_fixup_prefix3_orig, size) != 0);
98362306a36Sopenharmony_ci}
98462306a36Sopenharmony_ci#else
98562306a36Sopenharmony_cistatic inline void test_prefix_patching(void) {}
98662306a36Sopenharmony_cistatic inline void test_prefix_alt_patching(void) {}
98762306a36Sopenharmony_cistatic inline void test_prefix_word_alt_patching(void) {}
98862306a36Sopenharmony_ci#endif /* CONFIG_PPC64 */
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_cistatic int __init test_feature_fixups(void)
99162306a36Sopenharmony_ci{
99262306a36Sopenharmony_ci	printk(KERN_DEBUG "Running feature fixup self-tests ...\n");
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci	test_basic_patching();
99562306a36Sopenharmony_ci	test_alternative_patching();
99662306a36Sopenharmony_ci	test_alternative_case_too_big();
99762306a36Sopenharmony_ci	test_alternative_case_too_small();
99862306a36Sopenharmony_ci	test_alternative_case_with_branch();
99962306a36Sopenharmony_ci	test_alternative_case_with_external_branch();
100062306a36Sopenharmony_ci	test_alternative_case_with_branch_to_end();
100162306a36Sopenharmony_ci	test_cpu_macros();
100262306a36Sopenharmony_ci	test_fw_macros();
100362306a36Sopenharmony_ci	test_lwsync_macros();
100462306a36Sopenharmony_ci	test_prefix_patching();
100562306a36Sopenharmony_ci	test_prefix_alt_patching();
100662306a36Sopenharmony_ci	test_prefix_word_alt_patching();
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	return 0;
100962306a36Sopenharmony_ci}
101062306a36Sopenharmony_cilate_initcall(test_feature_fixups);
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci#endif /* CONFIG_FTR_FIXUP_SELFTEST */
1013