162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright 2008 Michael Ellerman, IBM Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kprobes.h>
762306a36Sopenharmony_ci#include <linux/mmu_context.h>
862306a36Sopenharmony_ci#include <linux/random.h>
962306a36Sopenharmony_ci#include <linux/vmalloc.h>
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/cpuhotplug.h>
1262306a36Sopenharmony_ci#include <linux/uaccess.h>
1362306a36Sopenharmony_ci#include <linux/jump_label.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include <asm/debug.h>
1662306a36Sopenharmony_ci#include <asm/pgalloc.h>
1762306a36Sopenharmony_ci#include <asm/tlb.h>
1862306a36Sopenharmony_ci#include <asm/tlbflush.h>
1962306a36Sopenharmony_ci#include <asm/page.h>
2062306a36Sopenharmony_ci#include <asm/code-patching.h>
2162306a36Sopenharmony_ci#include <asm/inst.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic int __patch_instruction(u32 *exec_addr, ppc_inst_t instr, u32 *patch_addr)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	if (!ppc_inst_prefixed(instr)) {
2662306a36Sopenharmony_ci		u32 val = ppc_inst_val(instr);
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci		__put_kernel_nofault(patch_addr, &val, u32, failed);
2962306a36Sopenharmony_ci	} else {
3062306a36Sopenharmony_ci		u64 val = ppc_inst_as_ulong(instr);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci		__put_kernel_nofault(patch_addr, &val, u64, failed);
3362306a36Sopenharmony_ci	}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	asm ("dcbst 0, %0; sync; icbi 0,%1; sync; isync" :: "r" (patch_addr),
3662306a36Sopenharmony_ci							    "r" (exec_addr));
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	return 0;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cifailed:
4162306a36Sopenharmony_ci	return -EPERM;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciint raw_patch_instruction(u32 *addr, ppc_inst_t instr)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	return __patch_instruction(addr, instr, addr);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct patch_context {
5062306a36Sopenharmony_ci	union {
5162306a36Sopenharmony_ci		struct vm_struct *area;
5262306a36Sopenharmony_ci		struct mm_struct *mm;
5362306a36Sopenharmony_ci	};
5462306a36Sopenharmony_ci	unsigned long addr;
5562306a36Sopenharmony_ci	pte_t *pte;
5662306a36Sopenharmony_ci};
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic DEFINE_PER_CPU(struct patch_context, cpu_patching_context);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic int map_patch_area(void *addr, unsigned long text_poke_addr);
6162306a36Sopenharmony_cistatic void unmap_patch_area(unsigned long addr);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic bool mm_patch_enabled(void)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	return IS_ENABLED(CONFIG_SMP) && radix_enabled();
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * The following applies for Radix MMU. Hash MMU has different requirements,
7062306a36Sopenharmony_ci * and so is not supported.
7162306a36Sopenharmony_ci *
7262306a36Sopenharmony_ci * Changing mm requires context synchronising instructions on both sides of
7362306a36Sopenharmony_ci * the context switch, as well as a hwsync between the last instruction for
7462306a36Sopenharmony_ci * which the address of an associated storage access was translated using
7562306a36Sopenharmony_ci * the current context.
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * switch_mm_irqs_off() performs an isync after the context switch. It is
7862306a36Sopenharmony_ci * the responsibility of the caller to perform the CSI and hwsync before
7962306a36Sopenharmony_ci * starting/stopping the temp mm.
8062306a36Sopenharmony_ci */
8162306a36Sopenharmony_cistatic struct mm_struct *start_using_temp_mm(struct mm_struct *temp_mm)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct mm_struct *orig_mm = current->active_mm;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	lockdep_assert_irqs_disabled();
8662306a36Sopenharmony_ci	switch_mm_irqs_off(orig_mm, temp_mm, current);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	WARN_ON(!mm_is_thread_local(temp_mm));
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	suspend_breakpoints();
9162306a36Sopenharmony_ci	return orig_mm;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void stop_using_temp_mm(struct mm_struct *temp_mm,
9562306a36Sopenharmony_ci			       struct mm_struct *orig_mm)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	lockdep_assert_irqs_disabled();
9862306a36Sopenharmony_ci	switch_mm_irqs_off(temp_mm, orig_mm, current);
9962306a36Sopenharmony_ci	restore_breakpoints();
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int text_area_cpu_up(unsigned int cpu)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct vm_struct *area;
10562306a36Sopenharmony_ci	unsigned long addr;
10662306a36Sopenharmony_ci	int err;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	area = get_vm_area(PAGE_SIZE, VM_ALLOC);
10962306a36Sopenharmony_ci	if (!area) {
11062306a36Sopenharmony_ci		WARN_ONCE(1, "Failed to create text area for cpu %d\n",
11162306a36Sopenharmony_ci			cpu);
11262306a36Sopenharmony_ci		return -1;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	// Map/unmap the area to ensure all page tables are pre-allocated
11662306a36Sopenharmony_ci	addr = (unsigned long)area->addr;
11762306a36Sopenharmony_ci	err = map_patch_area(empty_zero_page, addr);
11862306a36Sopenharmony_ci	if (err)
11962306a36Sopenharmony_ci		return err;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	unmap_patch_area(addr);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.area, area);
12462306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.addr, addr);
12562306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.pte, virt_to_kpte(addr));
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int text_area_cpu_down(unsigned int cpu)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	free_vm_area(this_cpu_read(cpu_patching_context.area));
13362306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.area, NULL);
13462306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.addr, 0);
13562306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.pte, NULL);
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic void put_patching_mm(struct mm_struct *mm, unsigned long patching_addr)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct mmu_gather tlb;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	tlb_gather_mmu(&tlb, mm);
14462306a36Sopenharmony_ci	free_pgd_range(&tlb, patching_addr, patching_addr + PAGE_SIZE, 0, 0);
14562306a36Sopenharmony_ci	mmput(mm);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int text_area_cpu_up_mm(unsigned int cpu)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct mm_struct *mm;
15162306a36Sopenharmony_ci	unsigned long addr;
15262306a36Sopenharmony_ci	pte_t *pte;
15362306a36Sopenharmony_ci	spinlock_t *ptl;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	mm = mm_alloc();
15662306a36Sopenharmony_ci	if (WARN_ON(!mm))
15762306a36Sopenharmony_ci		goto fail_no_mm;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/*
16062306a36Sopenharmony_ci	 * Choose a random page-aligned address from the interval
16162306a36Sopenharmony_ci	 * [PAGE_SIZE .. DEFAULT_MAP_WINDOW - PAGE_SIZE].
16262306a36Sopenharmony_ci	 * The lower address bound is PAGE_SIZE to avoid the zero-page.
16362306a36Sopenharmony_ci	 */
16462306a36Sopenharmony_ci	addr = (1 + (get_random_long() % (DEFAULT_MAP_WINDOW / PAGE_SIZE - 2))) << PAGE_SHIFT;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/*
16762306a36Sopenharmony_ci	 * PTE allocation uses GFP_KERNEL which means we need to
16862306a36Sopenharmony_ci	 * pre-allocate the PTE here because we cannot do the
16962306a36Sopenharmony_ci	 * allocation during patching when IRQs are disabled.
17062306a36Sopenharmony_ci	 *
17162306a36Sopenharmony_ci	 * Using get_locked_pte() to avoid open coding, the lock
17262306a36Sopenharmony_ci	 * is unnecessary.
17362306a36Sopenharmony_ci	 */
17462306a36Sopenharmony_ci	pte = get_locked_pte(mm, addr, &ptl);
17562306a36Sopenharmony_ci	if (!pte)
17662306a36Sopenharmony_ci		goto fail_no_pte;
17762306a36Sopenharmony_ci	pte_unmap_unlock(pte, ptl);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.mm, mm);
18062306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.addr, addr);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return 0;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cifail_no_pte:
18562306a36Sopenharmony_ci	put_patching_mm(mm, addr);
18662306a36Sopenharmony_cifail_no_mm:
18762306a36Sopenharmony_ci	return -ENOMEM;
18862306a36Sopenharmony_ci}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_cistatic int text_area_cpu_down_mm(unsigned int cpu)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	put_patching_mm(this_cpu_read(cpu_patching_context.mm),
19362306a36Sopenharmony_ci			this_cpu_read(cpu_patching_context.addr));
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.mm, NULL);
19662306a36Sopenharmony_ci	this_cpu_write(cpu_patching_context.addr, 0);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic __ro_after_init DEFINE_STATIC_KEY_FALSE(poking_init_done);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_civoid __init poking_init(void)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int ret;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
20862306a36Sopenharmony_ci		return;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	if (mm_patch_enabled())
21162306a36Sopenharmony_ci		ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
21262306a36Sopenharmony_ci					"powerpc/text_poke_mm:online",
21362306a36Sopenharmony_ci					text_area_cpu_up_mm,
21462306a36Sopenharmony_ci					text_area_cpu_down_mm);
21562306a36Sopenharmony_ci	else
21662306a36Sopenharmony_ci		ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
21762306a36Sopenharmony_ci					"powerpc/text_poke:online",
21862306a36Sopenharmony_ci					text_area_cpu_up,
21962306a36Sopenharmony_ci					text_area_cpu_down);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	/* cpuhp_setup_state returns >= 0 on success */
22262306a36Sopenharmony_ci	if (WARN_ON(ret < 0))
22362306a36Sopenharmony_ci		return;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	static_branch_enable(&poking_init_done);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic unsigned long get_patch_pfn(void *addr)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_MODULES) && is_vmalloc_or_module_addr(addr))
23162306a36Sopenharmony_ci		return vmalloc_to_pfn(addr);
23262306a36Sopenharmony_ci	else
23362306a36Sopenharmony_ci		return __pa_symbol(addr) >> PAGE_SHIFT;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/*
23762306a36Sopenharmony_ci * This can be called for kernel text or a module.
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_cistatic int map_patch_area(void *addr, unsigned long text_poke_addr)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	unsigned long pfn = get_patch_pfn(addr);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	return map_kernel_page(text_poke_addr, (pfn << PAGE_SHIFT), PAGE_KERNEL);
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic void unmap_patch_area(unsigned long addr)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	pte_t *ptep;
24962306a36Sopenharmony_ci	pmd_t *pmdp;
25062306a36Sopenharmony_ci	pud_t *pudp;
25162306a36Sopenharmony_ci	p4d_t *p4dp;
25262306a36Sopenharmony_ci	pgd_t *pgdp;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	pgdp = pgd_offset_k(addr);
25562306a36Sopenharmony_ci	if (WARN_ON(pgd_none(*pgdp)))
25662306a36Sopenharmony_ci		return;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	p4dp = p4d_offset(pgdp, addr);
25962306a36Sopenharmony_ci	if (WARN_ON(p4d_none(*p4dp)))
26062306a36Sopenharmony_ci		return;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	pudp = pud_offset(p4dp, addr);
26362306a36Sopenharmony_ci	if (WARN_ON(pud_none(*pudp)))
26462306a36Sopenharmony_ci		return;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	pmdp = pmd_offset(pudp, addr);
26762306a36Sopenharmony_ci	if (WARN_ON(pmd_none(*pmdp)))
26862306a36Sopenharmony_ci		return;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	ptep = pte_offset_kernel(pmdp, addr);
27162306a36Sopenharmony_ci	if (WARN_ON(pte_none(*ptep)))
27262306a36Sopenharmony_ci		return;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/*
27562306a36Sopenharmony_ci	 * In hash, pte_clear flushes the tlb, in radix, we have to
27662306a36Sopenharmony_ci	 */
27762306a36Sopenharmony_ci	pte_clear(&init_mm, addr, ptep);
27862306a36Sopenharmony_ci	flush_tlb_kernel_range(addr, addr + PAGE_SIZE);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic int __do_patch_instruction_mm(u32 *addr, ppc_inst_t instr)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	int err;
28462306a36Sopenharmony_ci	u32 *patch_addr;
28562306a36Sopenharmony_ci	unsigned long text_poke_addr;
28662306a36Sopenharmony_ci	pte_t *pte;
28762306a36Sopenharmony_ci	unsigned long pfn = get_patch_pfn(addr);
28862306a36Sopenharmony_ci	struct mm_struct *patching_mm;
28962306a36Sopenharmony_ci	struct mm_struct *orig_mm;
29062306a36Sopenharmony_ci	spinlock_t *ptl;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	patching_mm = __this_cpu_read(cpu_patching_context.mm);
29362306a36Sopenharmony_ci	text_poke_addr = __this_cpu_read(cpu_patching_context.addr);
29462306a36Sopenharmony_ci	patch_addr = (u32 *)(text_poke_addr + offset_in_page(addr));
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	pte = get_locked_pte(patching_mm, text_poke_addr, &ptl);
29762306a36Sopenharmony_ci	if (!pte)
29862306a36Sopenharmony_ci		return -ENOMEM;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	__set_pte_at(patching_mm, text_poke_addr, pte, pfn_pte(pfn, PAGE_KERNEL), 0);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* order PTE update before use, also serves as the hwsync */
30362306a36Sopenharmony_ci	asm volatile("ptesync": : :"memory");
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* order context switch after arbitrary prior code */
30662306a36Sopenharmony_ci	isync();
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	orig_mm = start_using_temp_mm(patching_mm);
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	err = __patch_instruction(addr, instr, patch_addr);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* hwsync performed by __patch_instruction (sync) if successful */
31362306a36Sopenharmony_ci	if (err)
31462306a36Sopenharmony_ci		mb();  /* sync */
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	/* context synchronisation performed by __patch_instruction (isync or exception) */
31762306a36Sopenharmony_ci	stop_using_temp_mm(patching_mm, orig_mm);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	pte_clear(patching_mm, text_poke_addr, pte);
32062306a36Sopenharmony_ci	/*
32162306a36Sopenharmony_ci	 * ptesync to order PTE update before TLB invalidation done
32262306a36Sopenharmony_ci	 * by radix__local_flush_tlb_page_psize (in _tlbiel_va)
32362306a36Sopenharmony_ci	 */
32462306a36Sopenharmony_ci	local_flush_tlb_page_psize(patching_mm, text_poke_addr, mmu_virtual_psize);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	pte_unmap_unlock(pte, ptl);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	return err;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int __do_patch_instruction(u32 *addr, ppc_inst_t instr)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	int err;
33462306a36Sopenharmony_ci	u32 *patch_addr;
33562306a36Sopenharmony_ci	unsigned long text_poke_addr;
33662306a36Sopenharmony_ci	pte_t *pte;
33762306a36Sopenharmony_ci	unsigned long pfn = get_patch_pfn(addr);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	text_poke_addr = (unsigned long)__this_cpu_read(cpu_patching_context.addr) & PAGE_MASK;
34062306a36Sopenharmony_ci	patch_addr = (u32 *)(text_poke_addr + offset_in_page(addr));
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	pte = __this_cpu_read(cpu_patching_context.pte);
34362306a36Sopenharmony_ci	__set_pte_at(&init_mm, text_poke_addr, pte, pfn_pte(pfn, PAGE_KERNEL), 0);
34462306a36Sopenharmony_ci	/* See ptesync comment in radix__set_pte_at() */
34562306a36Sopenharmony_ci	if (radix_enabled())
34662306a36Sopenharmony_ci		asm volatile("ptesync": : :"memory");
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	err = __patch_instruction(addr, instr, patch_addr);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	pte_clear(&init_mm, text_poke_addr, pte);
35162306a36Sopenharmony_ci	flush_tlb_kernel_range(text_poke_addr, text_poke_addr + PAGE_SIZE);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return err;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ciint patch_instruction(u32 *addr, ppc_inst_t instr)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	int err;
35962306a36Sopenharmony_ci	unsigned long flags;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/*
36262306a36Sopenharmony_ci	 * During early early boot patch_instruction is called
36362306a36Sopenharmony_ci	 * when text_poke_area is not ready, but we still need
36462306a36Sopenharmony_ci	 * to allow patching. We just do the plain old patching
36562306a36Sopenharmony_ci	 */
36662306a36Sopenharmony_ci	if (!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) ||
36762306a36Sopenharmony_ci	    !static_branch_likely(&poking_init_done))
36862306a36Sopenharmony_ci		return raw_patch_instruction(addr, instr);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	local_irq_save(flags);
37162306a36Sopenharmony_ci	if (mm_patch_enabled())
37262306a36Sopenharmony_ci		err = __do_patch_instruction_mm(addr, instr);
37362306a36Sopenharmony_ci	else
37462306a36Sopenharmony_ci		err = __do_patch_instruction(addr, instr);
37562306a36Sopenharmony_ci	local_irq_restore(flags);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	return err;
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ciNOKPROBE_SYMBOL(patch_instruction);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ciint patch_branch(u32 *addr, unsigned long target, int flags)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	ppc_inst_t instr;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (create_branch(&instr, addr, target, flags))
38662306a36Sopenharmony_ci		return -ERANGE;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return patch_instruction(addr, instr);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci/*
39262306a36Sopenharmony_ci * Helper to check if a given instruction is a conditional branch
39362306a36Sopenharmony_ci * Derived from the conditional checks in analyse_instr()
39462306a36Sopenharmony_ci */
39562306a36Sopenharmony_cibool is_conditional_branch(ppc_inst_t instr)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	unsigned int opcode = ppc_inst_primary_opcode(instr);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (opcode == 16)       /* bc, bca, bcl, bcla */
40062306a36Sopenharmony_ci		return true;
40162306a36Sopenharmony_ci	if (opcode == 19) {
40262306a36Sopenharmony_ci		switch ((ppc_inst_val(instr) >> 1) & 0x3ff) {
40362306a36Sopenharmony_ci		case 16:        /* bclr, bclrl */
40462306a36Sopenharmony_ci		case 528:       /* bcctr, bcctrl */
40562306a36Sopenharmony_ci		case 560:       /* bctar, bctarl */
40662306a36Sopenharmony_ci			return true;
40762306a36Sopenharmony_ci		}
40862306a36Sopenharmony_ci	}
40962306a36Sopenharmony_ci	return false;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ciNOKPROBE_SYMBOL(is_conditional_branch);
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ciint create_cond_branch(ppc_inst_t *instr, const u32 *addr,
41462306a36Sopenharmony_ci		       unsigned long target, int flags)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	long offset;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	offset = target;
41962306a36Sopenharmony_ci	if (! (flags & BRANCH_ABSOLUTE))
42062306a36Sopenharmony_ci		offset = offset - (unsigned long)addr;
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	/* Check we can represent the target in the instruction format */
42362306a36Sopenharmony_ci	if (!is_offset_in_cond_branch_range(offset))
42462306a36Sopenharmony_ci		return 1;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	/* Mask out the flags and target, so they don't step on each other. */
42762306a36Sopenharmony_ci	*instr = ppc_inst(0x40000000 | (flags & 0x3FF0003) | (offset & 0xFFFC));
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return 0;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciint instr_is_relative_branch(ppc_inst_t instr)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	if (ppc_inst_val(instr) & BRANCH_ABSOLUTE)
43562306a36Sopenharmony_ci		return 0;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return instr_is_branch_iform(instr) || instr_is_branch_bform(instr);
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ciint instr_is_relative_link_branch(ppc_inst_t instr)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	return instr_is_relative_branch(instr) && (ppc_inst_val(instr) & BRANCH_SET_LINK);
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_cistatic unsigned long branch_iform_target(const u32 *instr)
44662306a36Sopenharmony_ci{
44762306a36Sopenharmony_ci	signed long imm;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	imm = ppc_inst_val(ppc_inst_read(instr)) & 0x3FFFFFC;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* If the top bit of the immediate value is set this is negative */
45262306a36Sopenharmony_ci	if (imm & 0x2000000)
45362306a36Sopenharmony_ci		imm -= 0x4000000;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	if ((ppc_inst_val(ppc_inst_read(instr)) & BRANCH_ABSOLUTE) == 0)
45662306a36Sopenharmony_ci		imm += (unsigned long)instr;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return (unsigned long)imm;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic unsigned long branch_bform_target(const u32 *instr)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	signed long imm;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	imm = ppc_inst_val(ppc_inst_read(instr)) & 0xFFFC;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* If the top bit of the immediate value is set this is negative */
46862306a36Sopenharmony_ci	if (imm & 0x8000)
46962306a36Sopenharmony_ci		imm -= 0x10000;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if ((ppc_inst_val(ppc_inst_read(instr)) & BRANCH_ABSOLUTE) == 0)
47262306a36Sopenharmony_ci		imm += (unsigned long)instr;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return (unsigned long)imm;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ciunsigned long branch_target(const u32 *instr)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	if (instr_is_branch_iform(ppc_inst_read(instr)))
48062306a36Sopenharmony_ci		return branch_iform_target(instr);
48162306a36Sopenharmony_ci	else if (instr_is_branch_bform(ppc_inst_read(instr)))
48262306a36Sopenharmony_ci		return branch_bform_target(instr);
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	return 0;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ciint translate_branch(ppc_inst_t *instr, const u32 *dest, const u32 *src)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	unsigned long target;
49062306a36Sopenharmony_ci	target = branch_target(src);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (instr_is_branch_iform(ppc_inst_read(src)))
49362306a36Sopenharmony_ci		return create_branch(instr, dest, target,
49462306a36Sopenharmony_ci				     ppc_inst_val(ppc_inst_read(src)));
49562306a36Sopenharmony_ci	else if (instr_is_branch_bform(ppc_inst_read(src)))
49662306a36Sopenharmony_ci		return create_cond_branch(instr, dest, target,
49762306a36Sopenharmony_ci					  ppc_inst_val(ppc_inst_read(src)));
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	return 1;
50062306a36Sopenharmony_ci}
501