@@ -12,11 +12,11 @@ config ARM_32
config ARM_64
def_bool y
depends on 64BIT
- select HAS_ALTERNATIVE
select HAS_GICV3
config ARM
def_bool y
+ select HAS_ALTERNATIVE
select HAS_ARM_HDLCD
select HAS_DEVICE_TREE
select HAS_MEM_ACCESS
@@ -4,6 +4,7 @@ obj-$(EARLY_PRINTK) += debug.o
obj-y += domctl.o
obj-y += domain.o
obj-y += entry.o
+obj-y += insn.o
obj-$(CONFIG_LIVEPATCH) += livepatch.o
obj-y += proc-v7.o proc-caxx.o
obj-y += smpboot.o
new file mode 100644
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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/lib.h>
+#include <xen/bitops.h>
+#include <xen/sizes.h>
+#include <asm/insn.h>
+
+/* Mask of branch instructions' immediate. */
+#define BRANCH_INSN_IMM_MASK GENMASK(23, 0)
+/* Shift of branch instructions' immediate. */
+#define BRANCH_INSN_IMM_SHIFT 0
+
+static uint32_t branch_insn_encode_immediate(uint32_t insn, int32_t offset)
+{
+ uint32_t imm;
+
+ /*
+ * Encode the offset to imm. All ARM32 instructions must be word aligned.
+ * Therefore the offset value's bits [1:0] equal to zero.
+ * (see ARM DDI 0406C.c A8.8.18/A8.8.25 for more encode/decode details
+ * about ARM32 branch instructions)
+ */
+ imm = ((offset >> 2) & BRANCH_INSN_IMM_MASK) << BRANCH_INSN_IMM_SHIFT;
+
+ /* Update the immediate field. */
+ insn &= ~(BRANCH_INSN_IMM_MASK << BRANCH_INSN_IMM_SHIFT);
+ insn |= imm;
+
+ return insn;
+}
+
+/*
+ * Decode the branch offset from a branch instruction's imm field.
+ * The branch offset is a signed value, so it can be used to compute
+ * a new branch target.
+ */
+int32_t aarch32_get_branch_offset(uint32_t insn)
+{
+ uint32_t imm;
+
+ /* Retrieve imm from branch instruction. */
+ imm = ( insn >> BRANCH_INSN_IMM_SHIFT ) & BRANCH_INSN_IMM_MASK;
+
+ /*
+ * Check the imm signed bit. If the imm is a negative value, we
+ * have to extend the imm to a full 32 bit negative value.
+ */
+ if ( imm & BIT(23) )
+ imm |= GENMASK(31, 24);
+
+ return (int32_t)(imm << 2);
+}
+
+/*
+ * Encode the displacement of a branch in the imm field and return the
+ * updated instruction.
+ */
+uint32_t aarch32_set_branch_offset(uint32_t insn, int32_t offset)
+{
+ /* B/BL support [-32M, 32M) offset (see ARM DDI 0406C.c A4.3). */
+ if ( offset < -SZ_32M || offset >= SZ_32M )
+ {
+ printk(XENLOG_ERR
+ "%s: new branch offset out of range.\n", __func__);
+ return BUG_OPCODE;
+ }
+
+ return branch_insn_encode_immediate(insn, offset);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
@@ -25,6 +25,7 @@
#include <xen/livepatch.h>
#include <xen/livepatch_payload.h>
+#include <asm/alternative.h>
#include <asm/event.h>
/*
new file mode 100644
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 ARM Ltd.
+ *
+ * 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_ARM32_INSN
+#define __ARCH_ARM_ARM32_INSN
+
+#include <xen/types.h>
+
+#define __CONDITIONAL_INSN(insn) (((insn) >> 28) != 0xf)
+
+#define __AARCH32_INSN_FUNCS(abbr, mask, val) \
+static always_inline bool_t aarch32_insn_is_##abbr(uint32_t code) \
+{ \
+ return (code & (mask)) == (val); \
+}
+
+/* Conditional branch instructions */
+__AARCH32_INSN_FUNCS(b, 0x0F000000, 0x0A000000)
+__AARCH32_INSN_FUNCS(bl, 0x0F000000, 0x0B000000)
+
+/* Unconditional branch instructions */
+/*
+ * From ARM DDI 0406C.c Section A8.8.25. We can see blx has a H bit.
+ * In an ARM/Thumb instructions mixed running environment, this bit
+ * can be 1 or 0. Although Xen is only using the ARM instructions
+ * and the H bit is always 0. We mask this bit to catch both of these
+ * two encodings for future-proof.
+ */
+__AARCH32_INSN_FUNCS(blx, 0x0E000000, 0x0A000000)
+
+int32_t aarch32_get_branch_offset(uint32_t insn);
+uint32_t aarch32_set_branch_offset(uint32_t insn, int32_t offset);
+
+/* Wrapper for common code */
+static inline bool insn_is_branch_imm(uint32_t insn)
+{
+ /* Check conditional branch instructions */
+ if ( __CONDITIONAL_INSN(insn) )
+ return ( aarch32_insn_is_b(insn) || aarch32_insn_is_bl(insn) );
+
+ /* Check unconditional branch instructions */
+ return aarch32_insn_is_blx(insn);
+}
+
+static inline int32_t insn_get_branch_offset(uint32_t insn)
+{
+ return aarch32_get_branch_offset(insn);
+}
+
+static inline uint32_t insn_set_branch_offset(uint32_t insn, int32_t offset)
+{
+ return aarch32_set_branch_offset(insn, offset);
+}
+
+#endif /* !__ARCH_ARM_ARM32_INSN */
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
@@ -5,6 +5,8 @@
#if defined(CONFIG_ARM_64)
# include <asm/arm64/insn.h>
+#elif defined(CONFIG_ARM_32)
+# include <asm/arm32/insn.h>
#else
# error "unknown ARM variant"
#endif