[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v4 08/15] xen/arm: arm64: Add helpers to decode and encode branch instructions



We may need to update branch instruction when patching Xen.

The code has been imported from the files arch/arm64/kernel/insn.c
and arch/arm64/include/asm/insn.h in Linux v4.6.

Note that only the necessary helpers have been imported.

Signed-off-by: Julien Grall <julien.grall@xxxxxxx>
Acked-by: Stefano Stabellini <sstabellini@xxxxxxxxxx>

---
    Changes in v3:
        - Add Stefano's acked-by

    Changes in v2:
        - Re-sync with Linux v4.6
        - Drop any reference to arm32
---
 xen/arch/arm/arm64/Makefile      |   1 +
 xen/arch/arm/arm64/insn.c        | 219 +++++++++++++++++++++++++++++++++++++++
 xen/include/asm-arm/arm64/insn.h |  72 +++++++++++++
 xen/include/asm-arm/insn.h       |  20 ++++
 4 files changed, 312 insertions(+)
 create mode 100644 xen/arch/arm/arm64/insn.c
 create mode 100644 xen/include/asm-arm/arm64/insn.h
 create mode 100644 xen/include/asm-arm/insn.h

diff --git a/xen/arch/arm/arm64/Makefile b/xen/arch/arm/arm64/Makefile
index 39c6ac6..c1fa43f 100644
--- a/xen/arch/arm/arm64/Makefile
+++ b/xen/arch/arm/arm64/Makefile
@@ -5,6 +5,7 @@ obj-$(EARLY_PRINTK) += debug.o
 obj-y += domctl.o
 obj-y += domain.o
 obj-y += entry.o
+obj-y += insn.o
 obj-y += smpboot.o
 obj-y += traps.o
 obj-y += vfp.o
diff --git a/xen/arch/arm/arm64/insn.c b/xen/arch/arm/arm64/insn.c
new file mode 100644
index 0000000..12b4d96
--- /dev/null
+++ b/xen/arch/arm/arm64/insn.c
@@ -0,0 +1,219 @@
+/*
+ * Based on Linux v4.6 arch/arm64/kernel.ins.c
+ *
+ * Copyright (C) 2013 Huawei Ltd.
+ * Author: Jiang Liu <liuj97@xxxxxxxxx>
+ *
+ * Copyright (C) 2014-2016 Zi Shen Lim <zlim.lnx@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <xen/config.h>
+#include <xen/types.h>
+#include <xen/lib.h>
+#include <xen/errno.h>
+#include <xen/sizes.h>
+#include <xen/bitops.h>
+#include <asm/insn.h>
+#include <asm/arm64/insn.h>
+
+#define __kprobes
+#define pr_err(fmt, ...) printk(XENLOG_ERR fmt, ## __VA_ARGS__)
+
+bool aarch64_insn_is_branch_imm(u32 insn)
+{
+       return (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn) ||
+               aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn) ||
+               aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+               aarch64_insn_is_bcond(insn));
+}
+
+static int __kprobes aarch64_get_imm_shift_mask(enum aarch64_insn_imm_type 
type,
+                                               u32 *maskp, int *shiftp)
+{
+       u32 mask;
+       int shift;
+
+       switch (type) {
+       case AARCH64_INSN_IMM_26:
+               mask = BIT(26) - 1;
+               shift = 0;
+               break;
+       case AARCH64_INSN_IMM_19:
+               mask = BIT(19) - 1;
+               shift = 5;
+               break;
+       case AARCH64_INSN_IMM_16:
+               mask = BIT(16) - 1;
+               shift = 5;
+               break;
+       case AARCH64_INSN_IMM_14:
+               mask = BIT(14) - 1;
+               shift = 5;
+               break;
+       case AARCH64_INSN_IMM_12:
+               mask = BIT(12) - 1;
+               shift = 10;
+               break;
+       case AARCH64_INSN_IMM_9:
+               mask = BIT(9) - 1;
+               shift = 12;
+               break;
+       case AARCH64_INSN_IMM_7:
+               mask = BIT(7) - 1;
+               shift = 15;
+               break;
+       case AARCH64_INSN_IMM_6:
+       case AARCH64_INSN_IMM_S:
+               mask = BIT(6) - 1;
+               shift = 10;
+               break;
+       case AARCH64_INSN_IMM_R:
+               mask = BIT(6) - 1;
+               shift = 16;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *maskp = mask;
+       *shiftp = shift;
+
+       return 0;
+}
+
+#define ADR_IMM_HILOSPLIT      2
+#define ADR_IMM_SIZE           SZ_2M
+#define ADR_IMM_LOMASK         ((1 << ADR_IMM_HILOSPLIT) - 1)
+#define ADR_IMM_HIMASK         ((ADR_IMM_SIZE >> ADR_IMM_HILOSPLIT) - 1)
+#define ADR_IMM_LOSHIFT                29
+#define ADR_IMM_HISHIFT                5
+
+u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn)
+{
+       u32 immlo, immhi, mask;
+       int shift;
+
+       switch (type) {
+       case AARCH64_INSN_IMM_ADR:
+               shift = 0;
+               immlo = (insn >> ADR_IMM_LOSHIFT) & ADR_IMM_LOMASK;
+               immhi = (insn >> ADR_IMM_HISHIFT) & ADR_IMM_HIMASK;
+               insn = (immhi << ADR_IMM_HILOSPLIT) | immlo;
+               mask = ADR_IMM_SIZE - 1;
+               break;
+       default:
+               if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
+                       pr_err("aarch64_insn_decode_immediate: unknown 
immediate encoding %d\n",
+                              type);
+                       return 0;
+               }
+       }
+
+       return (insn >> shift) & mask;
+}
+
+u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
+                                 u32 insn, u64 imm)
+{
+       u32 immlo, immhi, mask;
+       int shift;
+
+       if (insn == AARCH64_BREAK_FAULT)
+               return AARCH64_BREAK_FAULT;
+
+       switch (type) {
+       case AARCH64_INSN_IMM_ADR:
+               shift = 0;
+               immlo = (imm & ADR_IMM_LOMASK) << ADR_IMM_LOSHIFT;
+               imm >>= ADR_IMM_HILOSPLIT;
+               immhi = (imm & ADR_IMM_HIMASK) << ADR_IMM_HISHIFT;
+               imm = immlo | immhi;
+               mask = ((ADR_IMM_LOMASK << ADR_IMM_LOSHIFT) |
+                       (ADR_IMM_HIMASK << ADR_IMM_HISHIFT));
+               break;
+       default:
+               if (aarch64_get_imm_shift_mask(type, &mask, &shift) < 0) {
+                       pr_err("aarch64_insn_encode_immediate: unknown 
immediate encoding %d\n",
+                              type);
+                       return AARCH64_BREAK_FAULT;
+               }
+       }
+
+       /* Update the immediate field. */
+       insn &= ~(mask << shift);
+       insn |= (imm & mask) << shift;
+
+       return insn;
+}
+
+/*
+ * Decode the imm field of a branch, and return the byte offset as a
+ * signed value (so it can be used when computing a new branch
+ * target).
+ */
+s32 aarch64_get_branch_offset(u32 insn)
+{
+       s32 imm;
+
+       if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) {
+               imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_26, insn);
+               return (imm << 6) >> 4;
+       }
+
+       if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+           aarch64_insn_is_bcond(insn)) {
+               imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_19, insn);
+               return (imm << 13) >> 11;
+       }
+
+       if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn)) {
+               imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_14, insn);
+               return (imm << 18) >> 16;
+       }
+
+       /* Unhandled instruction */
+       BUG();
+}
+
+/*
+ * Encode the displacement of a branch in the imm field and return the
+ * updated instruction.
+ */
+u32 aarch64_set_branch_offset(u32 insn, s32 offset)
+{
+       if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn))
+               return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_26, insn,
+                                                    offset >> 2);
+
+       if (aarch64_insn_is_cbz(insn) || aarch64_insn_is_cbnz(insn) ||
+           aarch64_insn_is_bcond(insn))
+               return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_19, insn,
+                                                    offset >> 2);
+
+       if (aarch64_insn_is_tbz(insn) || aarch64_insn_is_tbnz(insn))
+               return aarch64_insn_encode_immediate(AARCH64_INSN_IMM_14, insn,
+                                                    offset >> 2);
+
+       /* Unhandled instruction */
+       BUG();
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/xen/include/asm-arm/arm64/insn.h b/xen/include/asm-arm/arm64/insn.h
new file mode 100644
index 0000000..cfcdbe9
--- /dev/null
+++ b/xen/include/asm-arm/arm64/insn.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 Huawei Ltd.
+ * Author: Jiang Liu <liuj97@xxxxxxxxx>
+ *
+ * Copyright (C) 2014 Zi Shen Lim <zlim.lnx@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ARCH_ARM_ARM64_INSN
+#define __ARCH_ARM_ARM64_INSN
+
+#include <xen/config.h>
+#include <xen/types.h>
+#include <xen/stdbool.h>
+
+enum aarch64_insn_imm_type {
+       AARCH64_INSN_IMM_ADR,
+       AARCH64_INSN_IMM_26,
+       AARCH64_INSN_IMM_19,
+       AARCH64_INSN_IMM_16,
+       AARCH64_INSN_IMM_14,
+       AARCH64_INSN_IMM_12,
+       AARCH64_INSN_IMM_9,
+       AARCH64_INSN_IMM_7,
+       AARCH64_INSN_IMM_6,
+       AARCH64_INSN_IMM_S,
+       AARCH64_INSN_IMM_R,
+       AARCH64_INSN_IMM_MAX
+};
+
+#define        __AARCH64_INSN_FUNCS(abbr, mask, val)   \
+static always_inline bool_t aarch64_insn_is_##abbr(u32 code) \
+{ return (code & (mask)) == (val); } \
+static always_inline u32 aarch64_insn_get_##abbr##_value(void) \
+{ return (val); }
+
+__AARCH64_INSN_FUNCS(b,                0xFC000000, 0x14000000)
+__AARCH64_INSN_FUNCS(bl,       0xFC000000, 0x94000000)
+__AARCH64_INSN_FUNCS(cbz,      0x7F000000, 0x34000000)
+__AARCH64_INSN_FUNCS(cbnz,     0x7F000000, 0x35000000)
+__AARCH64_INSN_FUNCS(tbz,      0x7F000000, 0x36000000)
+__AARCH64_INSN_FUNCS(tbnz,     0x7F000000, 0x37000000)
+__AARCH64_INSN_FUNCS(bcond,    0xFF000010, 0x54000000)
+
+bool aarch64_insn_is_branch_imm(u32 insn);
+
+u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn);
+u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
+                                 u32 insn, u64 imm);
+
+s32 aarch64_get_branch_offset(u32 insn);
+u32 aarch64_set_branch_offset(u32 insn, s32 offset);
+
+#endif /* !__ARCH_ARM_ARM64_INSN */
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/xen/include/asm-arm/insn.h b/xen/include/asm-arm/insn.h
new file mode 100644
index 0000000..a205ceb
--- /dev/null
+++ b/xen/include/asm-arm/insn.h
@@ -0,0 +1,20 @@
+#ifndef __ARCH_ARM_INSN
+#define __ARCH_ARM_INSN
+
+#include <xen/types.h>
+
+#if defined(CONFIG_ARM_64)
+# include <asm/arm64/insn.h>
+#else
+# error "unknown ARM variant"
+#endif
+
+#endif /* !__ARCH_ARM_INSN */
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 8
+ * indent-tabs-mode: t
+ * End:
+ */
-- 
1.9.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.