18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/bug.h>
38c2ecf20Sopenharmony_ci#include <linux/kernel.h>
48c2ecf20Sopenharmony_ci#include <asm/opcodes.h>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_cistatic unsigned long __arm_gen_branch_thumb2(unsigned long pc,
78c2ecf20Sopenharmony_ci					     unsigned long addr, bool link,
88c2ecf20Sopenharmony_ci					     bool warn)
98c2ecf20Sopenharmony_ci{
108c2ecf20Sopenharmony_ci	unsigned long s, j1, j2, i1, i2, imm10, imm11;
118c2ecf20Sopenharmony_ci	unsigned long first, second;
128c2ecf20Sopenharmony_ci	long offset;
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci	offset = (long)addr - (long)(pc + 4);
158c2ecf20Sopenharmony_ci	if (offset < -16777216 || offset > 16777214) {
168c2ecf20Sopenharmony_ci		WARN_ON_ONCE(warn);
178c2ecf20Sopenharmony_ci		return 0;
188c2ecf20Sopenharmony_ci	}
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	s	= (offset >> 24) & 0x1;
218c2ecf20Sopenharmony_ci	i1	= (offset >> 23) & 0x1;
228c2ecf20Sopenharmony_ci	i2	= (offset >> 22) & 0x1;
238c2ecf20Sopenharmony_ci	imm10	= (offset >> 12) & 0x3ff;
248c2ecf20Sopenharmony_ci	imm11	= (offset >>  1) & 0x7ff;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	j1 = (!i1) ^ s;
278c2ecf20Sopenharmony_ci	j2 = (!i2) ^ s;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	first = 0xf000 | (s << 10) | imm10;
308c2ecf20Sopenharmony_ci	second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11;
318c2ecf20Sopenharmony_ci	if (link)
328c2ecf20Sopenharmony_ci		second |= 1 << 14;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	return __opcode_thumb32_compose(first, second);
358c2ecf20Sopenharmony_ci}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic unsigned long __arm_gen_branch_arm(unsigned long pc, unsigned long addr,
388c2ecf20Sopenharmony_ci					  bool link, bool warn)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned long opcode = 0xea000000;
418c2ecf20Sopenharmony_ci	long offset;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	if (link)
448c2ecf20Sopenharmony_ci		opcode |= 1 << 24;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	offset = (long)addr - (long)(pc + 8);
478c2ecf20Sopenharmony_ci	if (unlikely(offset < -33554432 || offset > 33554428)) {
488c2ecf20Sopenharmony_ci		WARN_ON_ONCE(warn);
498c2ecf20Sopenharmony_ci		return 0;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	offset = (offset >> 2) & 0x00ffffff;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return opcode | offset;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ciunsigned long
588c2ecf20Sopenharmony_ci__arm_gen_branch(unsigned long pc, unsigned long addr, bool link, bool warn)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_THUMB2_KERNEL))
618c2ecf20Sopenharmony_ci		return __arm_gen_branch_thumb2(pc, addr, link, warn);
628c2ecf20Sopenharmony_ci	else
638c2ecf20Sopenharmony_ci		return __arm_gen_branch_arm(pc, addr, link, warn);
648c2ecf20Sopenharmony_ci}
65