1 /*
2 * Copyright (C) 2017 ARM Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16 #include <xen/lib.h>
17 #include <xen/bitops.h>
18 #include <xen/sizes.h>
19 #include <asm/insn.h>
20
21 /* Mask of branch instructions' immediate. */
22 #define BRANCH_INSN_IMM_MASK GENMASK(23, 0)
23 /* Shift of branch instructions' immediate. */
24 #define BRANCH_INSN_IMM_SHIFT 0
25
branch_insn_encode_immediate(uint32_t insn,int32_t offset)26 static uint32_t branch_insn_encode_immediate(uint32_t insn, int32_t offset)
27 {
28 uint32_t imm;
29
30 /*
31 * Encode the offset to imm. All ARM32 instructions must be word aligned.
32 * Therefore the offset value's bits [1:0] equal to zero.
33 * (see ARM DDI 0406C.c A8.8.18/A8.8.25 for more encode/decode details
34 * about ARM32 branch instructions)
35 */
36 imm = ((offset >> 2) & BRANCH_INSN_IMM_MASK) << BRANCH_INSN_IMM_SHIFT;
37
38 /* Update the immediate field. */
39 insn &= ~(BRANCH_INSN_IMM_MASK << BRANCH_INSN_IMM_SHIFT);
40 insn |= imm;
41
42 return insn;
43 }
44
45 /*
46 * Decode the branch offset from a branch instruction's imm field.
47 * The branch offset is a signed value, so it can be used to compute
48 * a new branch target.
49 */
aarch32_get_branch_offset(uint32_t insn)50 int32_t aarch32_get_branch_offset(uint32_t insn)
51 {
52 uint32_t imm;
53
54 /* Retrieve imm from branch instruction. */
55 imm = ( insn >> BRANCH_INSN_IMM_SHIFT ) & BRANCH_INSN_IMM_MASK;
56
57 /*
58 * Check the imm signed bit. If the imm is a negative value, we
59 * have to extend the imm to a full 32 bit negative value.
60 */
61 if ( imm & BIT(23) )
62 imm |= GENMASK(31, 24);
63
64 return (int32_t)(imm << 2);
65 }
66
67 /*
68 * Encode the displacement of a branch in the imm field and return the
69 * updated instruction.
70 */
aarch32_set_branch_offset(uint32_t insn,int32_t offset)71 uint32_t aarch32_set_branch_offset(uint32_t insn, int32_t offset)
72 {
73 /* B/BL support [-32M, 32M) offset (see ARM DDI 0406C.c A4.3). */
74 if ( offset < -SZ_32M || offset >= SZ_32M )
75 {
76 printk(XENLOG_ERR
77 "%s: new branch offset out of range.\n", __func__);
78 return BUG_OPCODE;
79 }
80
81 return branch_insn_encode_immediate(insn, offset);
82 }
83
84 /*
85 * Local variables:
86 * mode: C
87 * c-file-style: "BSD"
88 * c-basic-offset: 4
89 * indent-tabs-mode: nil
90 * End:
91 */
92