summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristoph Muellner <christoph.muellner@theobroma-systems.com>2018-05-15 23:38:32 +0200
committerChristoph Muellner <christoph.muellner@theobroma-systems.com>2018-05-23 13:00:50 +0200
commit4891e78649d273115d2943721d8974c72e0a15df (patch)
tree5cb368dccbbf1f13589ae94b71325fb1f5ebab71
parentf8a1367d86fa275ef1d8fcced8b151d01f5c1f09 (diff)
arm64: alternatives: Add support for adr/adrp with offset in alt block.
When using adr or adrp with an offset within the alt block, we can allow the instruction and we must not do any offset fixup. Signed-off-by: Christoph Muellner <christoph.muellner@theobroma-systems.com>
-rw-r--r--arch/arm64/kernel/alternative.c31
1 files changed, 24 insertions, 7 deletions
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 414288a558c8..fdcdb15251a3 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -87,15 +87,32 @@ static u32 get_alt_insn(struct alt_instr *alt, __le32 *insnptr, __le32 *altinsnp
s32 orig_offset, new_offset;
unsigned long target;
- /*
- * If we're replacing an adrp instruction, which uses PC-relative
- * immediate addressing, adjust the offset to reflect the new
- * PC. adrp operates on 4K aligned addresses.
- */
orig_offset = aarch64_insn_adrp_get_offset(insn);
target = align_down(altinsnptr, SZ_4K) + orig_offset;
- new_offset = target - align_down(insnptr, SZ_4K);
- insn = aarch64_insn_adrp_set_offset(insn, new_offset);
+
+ if (branch_insn_requires_update(alt, target)) {
+ /*
+ * If we're replacing an adrp instruction, which uses
+ * PC-relative immediate addressing, adjust the offset
+ * to reflect the new PC. adrp operates on 4K aligned
+ * addresses.
+ */
+ new_offset = target - align_down(insnptr, SZ_4K);
+ insn = aarch64_insn_adrp_set_offset(insn, new_offset);
+ }
+ } else if (aarch64_insn_is_adr(insn)) {
+ s32 offset = aarch64_insn_adr_get_offset(insn);
+ unsigned long target;
+
+ target = (unsigned long)altinsnptr + offset;
+
+ if (branch_insn_requires_update(alt, target)) {
+ /*
+ * Disallow adr instructions for targets outside
+ * of our alt block.
+ */
+ BUG();
+ }
} else if (aarch64_insn_uses_literal(insn)) {
/*
* Disallow patching unhandled instructions using PC relative