linux/arch/arm/kernel/insn.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/bug.h>
   3#include <linux/kernel.h>
   4#include <asm/opcodes.h>
   5
   6static unsigned long
   7__arm_gen_branch_thumb2(unsigned long pc, unsigned long addr, bool link)
   8{
   9        unsigned long s, j1, j2, i1, i2, imm10, imm11;
  10        unsigned long first, second;
  11        long offset;
  12
  13        offset = (long)addr - (long)(pc + 4);
  14        if (offset < -16777216 || offset > 16777214) {
  15                WARN_ON_ONCE(1);
  16                return 0;
  17        }
  18
  19        s       = (offset >> 24) & 0x1;
  20        i1      = (offset >> 23) & 0x1;
  21        i2      = (offset >> 22) & 0x1;
  22        imm10   = (offset >> 12) & 0x3ff;
  23        imm11   = (offset >>  1) & 0x7ff;
  24
  25        j1 = (!i1) ^ s;
  26        j2 = (!i2) ^ s;
  27
  28        first = 0xf000 | (s << 10) | imm10;
  29        second = 0x9000 | (j1 << 13) | (j2 << 11) | imm11;
  30        if (link)
  31                second |= 1 << 14;
  32
  33        return __opcode_thumb32_compose(first, second);
  34}
  35
  36static unsigned long
  37__arm_gen_branch_arm(unsigned long pc, unsigned long addr, bool link)
  38{
  39        unsigned long opcode = 0xea000000;
  40        long offset;
  41
  42        if (link)
  43                opcode |= 1 << 24;
  44
  45        offset = (long)addr - (long)(pc + 8);
  46        if (unlikely(offset < -33554432 || offset > 33554428)) {
  47                WARN_ON_ONCE(1);
  48                return 0;
  49        }
  50
  51        offset = (offset >> 2) & 0x00ffffff;
  52
  53        return opcode | offset;
  54}
  55
  56unsigned long
  57__arm_gen_branch(unsigned long pc, unsigned long addr, bool link)
  58{
  59        if (IS_ENABLED(CONFIG_THUMB2_KERNEL))
  60                return __arm_gen_branch_thumb2(pc, addr, link);
  61        else
  62                return __arm_gen_branch_arm(pc, addr, link);
  63}
  64