18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2019 Helge Deller <deller@gmx.de>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Based on arch/arm64/kernel/jump_label.c
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/jump_label.h>
98c2ecf20Sopenharmony_ci#include <linux/bug.h>
108c2ecf20Sopenharmony_ci#include <asm/alternative.h>
118c2ecf20Sopenharmony_ci#include <asm/patch.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_cistatic inline int reassemble_17(int as17)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	return (((as17 & 0x10000) >> 16) |
168c2ecf20Sopenharmony_ci		((as17 & 0x0f800) << 5) |
178c2ecf20Sopenharmony_ci		((as17 & 0x00400) >> 8) |
188c2ecf20Sopenharmony_ci		((as17 & 0x003ff) << 3));
198c2ecf20Sopenharmony_ci}
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_civoid arch_jump_label_transform(struct jump_entry *entry,
228c2ecf20Sopenharmony_ci			       enum jump_label_type type)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	void *addr = (void *)jump_entry_code(entry);
258c2ecf20Sopenharmony_ci	u32 insn;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	if (type == JUMP_LABEL_JMP) {
288c2ecf20Sopenharmony_ci		void *target = (void *)jump_entry_target(entry);
298c2ecf20Sopenharmony_ci		int distance = target - addr;
308c2ecf20Sopenharmony_ci		/*
318c2ecf20Sopenharmony_ci		 * Encode the PA1.1 "b,n" instruction with a 17-bit
328c2ecf20Sopenharmony_ci		 * displacement.  In case we hit the BUG(), we could use
338c2ecf20Sopenharmony_ci		 * another branch instruction with a 22-bit displacement on
348c2ecf20Sopenharmony_ci		 * 64-bit CPUs instead. But this seems sufficient for now.
358c2ecf20Sopenharmony_ci		 */
368c2ecf20Sopenharmony_ci		distance -= 8;
378c2ecf20Sopenharmony_ci		BUG_ON(distance > 262143 || distance < -262144);
388c2ecf20Sopenharmony_ci		insn = 0xe8000002 | reassemble_17(distance >> 2);
398c2ecf20Sopenharmony_ci	} else {
408c2ecf20Sopenharmony_ci		insn = INSN_NOP;
418c2ecf20Sopenharmony_ci	}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	patch_text(addr, insn);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_civoid arch_jump_label_transform_static(struct jump_entry *entry,
478c2ecf20Sopenharmony_ci				      enum jump_label_type type)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	/*
508c2ecf20Sopenharmony_ci	 * We use the architected NOP in arch_static_branch, so there's no
518c2ecf20Sopenharmony_ci	 * need to patch an identical NOP over the top of it here. The core
528c2ecf20Sopenharmony_ci	 * will call arch_jump_label_transform from a module notifier if the
538c2ecf20Sopenharmony_ci	 * NOP needs to be replaced by a branch.
548c2ecf20Sopenharmony_ci	 */
558c2ecf20Sopenharmony_ci}
56