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 * Copyright (c) 2010 Cavium Networks, Inc.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/jump_label.h>
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/memory.h>
1262306a36Sopenharmony_ci#include <linux/mutex.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/cpu.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/cacheflush.h>
1762306a36Sopenharmony_ci#include <asm/inst.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * Define parameters for the standard MIPS and the microMIPS jump
2162306a36Sopenharmony_ci * instruction encoding respectively:
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * - the ISA bit of the target, either 0 or 1 respectively,
2462306a36Sopenharmony_ci *
2562306a36Sopenharmony_ci * - the amount the jump target address is shifted right to fit in the
2662306a36Sopenharmony_ci *   immediate field of the machine instruction, either 2 or 1,
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * - the mask determining the size of the jump region relative to the
2962306a36Sopenharmony_ci *   delay-slot instruction, either 256MB or 128MB,
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci * - the jump target alignment, either 4 or 2 bytes.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci#define J_ISA_BIT	IS_ENABLED(CONFIG_CPU_MICROMIPS)
3462306a36Sopenharmony_ci#define J_RANGE_SHIFT	(2 - J_ISA_BIT)
3562306a36Sopenharmony_ci#define J_RANGE_MASK	((1ul << (26 + J_RANGE_SHIFT)) - 1)
3662306a36Sopenharmony_ci#define J_ALIGN_MASK	((1ul << J_RANGE_SHIFT) - 1)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_civoid arch_jump_label_transform(struct jump_entry *e,
3962306a36Sopenharmony_ci			       enum jump_label_type type)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	union mips_instruction *insn_p;
4262306a36Sopenharmony_ci	union mips_instruction insn;
4362306a36Sopenharmony_ci	long offset;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	insn_p = (union mips_instruction *)msk_isa16_mode(e->code);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	/* Target must have the right alignment and ISA must be preserved. */
4862306a36Sopenharmony_ci	BUG_ON((e->target & J_ALIGN_MASK) != J_ISA_BIT);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (type == JUMP_LABEL_JMP) {
5162306a36Sopenharmony_ci		if (!IS_ENABLED(CONFIG_CPU_MICROMIPS) && MIPS_ISA_REV >= 6) {
5262306a36Sopenharmony_ci			offset = e->target - ((unsigned long)insn_p + 4);
5362306a36Sopenharmony_ci			offset >>= 2;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci			/*
5662306a36Sopenharmony_ci			 * The branch offset must fit in the instruction's 26
5762306a36Sopenharmony_ci			 * bit field.
5862306a36Sopenharmony_ci			 */
5962306a36Sopenharmony_ci			WARN_ON((offset >= (long)BIT(25)) ||
6062306a36Sopenharmony_ci				(offset < -(long)BIT(25)));
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci			insn.j_format.opcode = bc6_op;
6362306a36Sopenharmony_ci			insn.j_format.target = offset;
6462306a36Sopenharmony_ci		} else {
6562306a36Sopenharmony_ci			/*
6662306a36Sopenharmony_ci			 * Jump only works within an aligned region its delay
6762306a36Sopenharmony_ci			 * slot is in.
6862306a36Sopenharmony_ci			 */
6962306a36Sopenharmony_ci			WARN_ON((e->target & ~J_RANGE_MASK) !=
7062306a36Sopenharmony_ci				((e->code + 4) & ~J_RANGE_MASK));
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci			insn.j_format.opcode = J_ISA_BIT ? mm_j32_op : j_op;
7362306a36Sopenharmony_ci			insn.j_format.target = e->target >> J_RANGE_SHIFT;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci	} else {
7662306a36Sopenharmony_ci		insn.word = 0; /* nop */
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	mutex_lock(&text_mutex);
8062306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_CPU_MICROMIPS)) {
8162306a36Sopenharmony_ci		insn_p->halfword[0] = insn.word >> 16;
8262306a36Sopenharmony_ci		insn_p->halfword[1] = insn.word;
8362306a36Sopenharmony_ci	} else
8462306a36Sopenharmony_ci		*insn_p = insn;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	flush_icache_range((unsigned long)insn_p,
8762306a36Sopenharmony_ci			   (unsigned long)insn_p + sizeof(*insn_p));
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	mutex_unlock(&text_mutex);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci#ifdef CONFIG_MODULES
9362306a36Sopenharmony_civoid jump_label_apply_nops(struct module *mod)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct jump_entry *iter_start = mod->jump_entries;
9662306a36Sopenharmony_ci	struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
9762306a36Sopenharmony_ci	struct jump_entry *iter;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* if the module doesn't have jump label entries, just return */
10062306a36Sopenharmony_ci	if (iter_start == iter_stop)
10162306a36Sopenharmony_ci		return;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	for (iter = iter_start; iter < iter_stop; iter++) {
10462306a36Sopenharmony_ci		/* Only write NOPs for arch_branch_static(). */
10562306a36Sopenharmony_ci		if (jump_label_init_type(iter) == JUMP_LABEL_NOP)
10662306a36Sopenharmony_ci			arch_jump_label_transform(iter, JUMP_LABEL_NOP);
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci#endif
110