summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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