diff options
Diffstat (limited to 'arch/mips/kernel/jump_label.c')
-rw-r--r-- | arch/mips/kernel/jump_label.c | 42 |
1 files changed, 32 insertions, 10 deletions
diff --git a/arch/mips/kernel/jump_label.c b/arch/mips/kernel/jump_label.c index 6001610cfe55..dda800e9e731 100644 --- a/arch/mips/kernel/jump_label.c +++ b/arch/mips/kernel/jump_label.c | |||
@@ -18,31 +18,53 @@ | |||
18 | 18 | ||
19 | #ifdef HAVE_JUMP_LABEL | 19 | #ifdef HAVE_JUMP_LABEL |
20 | 20 | ||
21 | #define J_RANGE_MASK ((1ul << 28) - 1) | 21 | /* |
22 | * Define parameters for the standard MIPS and the microMIPS jump | ||
23 | * instruction encoding respectively: | ||
24 | * | ||
25 | * - the ISA bit of the target, either 0 or 1 respectively, | ||
26 | * | ||
27 | * - the amount the jump target address is shifted right to fit in the | ||
28 | * immediate field of the machine instruction, either 2 or 1, | ||
29 | * | ||
30 | * - the mask determining the size of the jump region relative to the | ||
31 | * delay-slot instruction, either 256MB or 128MB, | ||
32 | * | ||
33 | * - the jump target alignment, either 4 or 2 bytes. | ||
34 | */ | ||
35 | #define J_ISA_BIT IS_ENABLED(CONFIG_CPU_MICROMIPS) | ||
36 | #define J_RANGE_SHIFT (2 - J_ISA_BIT) | ||
37 | #define J_RANGE_MASK ((1ul << (26 + J_RANGE_SHIFT)) - 1) | ||
38 | #define J_ALIGN_MASK ((1ul << J_RANGE_SHIFT) - 1) | ||
22 | 39 | ||
23 | void arch_jump_label_transform(struct jump_entry *e, | 40 | void arch_jump_label_transform(struct jump_entry *e, |
24 | enum jump_label_type type) | 41 | enum jump_label_type type) |
25 | { | 42 | { |
43 | union mips_instruction *insn_p; | ||
26 | union mips_instruction insn; | 44 | union mips_instruction insn; |
27 | union mips_instruction *insn_p = | ||
28 | (union mips_instruction *)(unsigned long)e->code; | ||
29 | 45 | ||
30 | /* Jump only works within a 256MB aligned region. */ | 46 | insn_p = (union mips_instruction *)msk_isa16_mode(e->code); |
31 | BUG_ON((e->target & ~J_RANGE_MASK) != (e->code & ~J_RANGE_MASK)); | 47 | |
48 | /* Jump only works within an aligned region its delay slot is in. */ | ||
49 | BUG_ON((e->target & ~J_RANGE_MASK) != ((e->code + 4) & ~J_RANGE_MASK)); | ||
32 | 50 | ||
33 | /* Target must have 4 byte alignment. */ | 51 | /* Target must have the right alignment and ISA must be preserved. */ |
34 | BUG_ON((e->target & 3) != 0); | 52 | BUG_ON((e->target & J_ALIGN_MASK) != J_ISA_BIT); |
35 | 53 | ||
36 | if (type == JUMP_LABEL_ENABLE) { | 54 | if (type == JUMP_LABEL_ENABLE) { |
37 | insn.j_format.opcode = j_op; | 55 | insn.j_format.opcode = J_ISA_BIT ? mm_j32_op : j_op; |
38 | insn.j_format.target = (e->target & J_RANGE_MASK) >> 2; | 56 | insn.j_format.target = e->target >> J_RANGE_SHIFT; |
39 | } else { | 57 | } else { |
40 | insn.word = 0; /* nop */ | 58 | insn.word = 0; /* nop */ |
41 | } | 59 | } |
42 | 60 | ||
43 | get_online_cpus(); | 61 | get_online_cpus(); |
44 | mutex_lock(&text_mutex); | 62 | mutex_lock(&text_mutex); |
45 | *insn_p = insn; | 63 | if (IS_ENABLED(CONFIG_CPU_MICROMIPS)) { |
64 | insn_p->halfword[0] = insn.word >> 16; | ||
65 | insn_p->halfword[1] = insn.word; | ||
66 | } else | ||
67 | *insn_p = insn; | ||
46 | 68 | ||
47 | flush_icache_range((unsigned long)insn_p, | 69 | flush_icache_range((unsigned long)insn_p, |
48 | (unsigned long)insn_p + sizeof(*insn_p)); | 70 | (unsigned long)insn_p + sizeof(*insn_p)); |