Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * This file is subject to the terms and conditions of the GNU General Public
0003  * License.  See the file "COPYING" in the main directory of this archive
0004  * for more details.
0005  *
0006  * Copyright (c) 2010 Cavium Networks, Inc.
0007  */
0008 
0009 #include <linux/jump_label.h>
0010 #include <linux/kernel.h>
0011 #include <linux/memory.h>
0012 #include <linux/mutex.h>
0013 #include <linux/types.h>
0014 #include <linux/cpu.h>
0015 
0016 #include <asm/cacheflush.h>
0017 #include <asm/inst.h>
0018 
0019 /*
0020  * Define parameters for the standard MIPS and the microMIPS jump
0021  * instruction encoding respectively:
0022  *
0023  * - the ISA bit of the target, either 0 or 1 respectively,
0024  *
0025  * - the amount the jump target address is shifted right to fit in the
0026  *   immediate field of the machine instruction, either 2 or 1,
0027  *
0028  * - the mask determining the size of the jump region relative to the
0029  *   delay-slot instruction, either 256MB or 128MB,
0030  *
0031  * - the jump target alignment, either 4 or 2 bytes.
0032  */
0033 #define J_ISA_BIT   IS_ENABLED(CONFIG_CPU_MICROMIPS)
0034 #define J_RANGE_SHIFT   (2 - J_ISA_BIT)
0035 #define J_RANGE_MASK    ((1ul << (26 + J_RANGE_SHIFT)) - 1)
0036 #define J_ALIGN_MASK    ((1ul << J_RANGE_SHIFT) - 1)
0037 
0038 void arch_jump_label_transform(struct jump_entry *e,
0039                    enum jump_label_type type)
0040 {
0041     union mips_instruction *insn_p;
0042     union mips_instruction insn;
0043     long offset;
0044 
0045     insn_p = (union mips_instruction *)msk_isa16_mode(e->code);
0046 
0047     /* Target must have the right alignment and ISA must be preserved. */
0048     BUG_ON((e->target & J_ALIGN_MASK) != J_ISA_BIT);
0049 
0050     if (type == JUMP_LABEL_JMP) {
0051         if (!IS_ENABLED(CONFIG_CPU_MICROMIPS) && MIPS_ISA_REV >= 6) {
0052             offset = e->target - ((unsigned long)insn_p + 4);
0053             offset >>= 2;
0054 
0055             /*
0056              * The branch offset must fit in the instruction's 26
0057              * bit field.
0058              */
0059             WARN_ON((offset >= BIT(25)) ||
0060                 (offset < -(long)BIT(25)));
0061 
0062             insn.j_format.opcode = bc6_op;
0063             insn.j_format.target = offset;
0064         } else {
0065             /*
0066              * Jump only works within an aligned region its delay
0067              * slot is in.
0068              */
0069             WARN_ON((e->target & ~J_RANGE_MASK) !=
0070                 ((e->code + 4) & ~J_RANGE_MASK));
0071 
0072             insn.j_format.opcode = J_ISA_BIT ? mm_j32_op : j_op;
0073             insn.j_format.target = e->target >> J_RANGE_SHIFT;
0074         }
0075     } else {
0076         insn.word = 0; /* nop */
0077     }
0078 
0079     mutex_lock(&text_mutex);
0080     if (IS_ENABLED(CONFIG_CPU_MICROMIPS)) {
0081         insn_p->halfword[0] = insn.word >> 16;
0082         insn_p->halfword[1] = insn.word;
0083     } else
0084         *insn_p = insn;
0085 
0086     flush_icache_range((unsigned long)insn_p,
0087                (unsigned long)insn_p + sizeof(*insn_p));
0088 
0089     mutex_unlock(&text_mutex);
0090 }
0091 
0092 #ifdef CONFIG_MODULES
0093 void jump_label_apply_nops(struct module *mod)
0094 {
0095     struct jump_entry *iter_start = mod->jump_entries;
0096     struct jump_entry *iter_stop = iter_start + mod->num_jump_entries;
0097     struct jump_entry *iter;
0098 
0099     /* if the module doesn't have jump label entries, just return */
0100     if (iter_start == iter_stop)
0101         return;
0102 
0103     for (iter = iter_start; iter < iter_stop; iter++) {
0104         /* Only write NOPs for arch_branch_static(). */
0105         if (jump_label_init_type(iter) == JUMP_LABEL_NOP)
0106             arch_jump_label_transform(iter, JUMP_LABEL_NOP);
0107     }
0108 }
0109 #endif