aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2015-05-01 08:48:17 -0400
committerWill Deacon <will.deacon@arm.com>2015-05-05 07:21:52 -0400
commitb9a95e85bbc56f168f078885f414f305b4589c4b (patch)
treecc6acb0ea753833355d3db50ab2b114997fb542f /arch/arm64
parent5ebe6afaf0057ac3eaeb98defd5456894b446d22 (diff)
Revert "arm64: alternative: Allow immediate branch as alternative instruction"
This reverts most of commit fef7f2b2010381c795ae43743ad31931cc58f5ad. It turns out that there are a couple of problems with the way we're fixing up branch instructions used as part of alternative instruction sequences: (1) If the branch target is also in the alternative sequence, we'll generate a branch into the .altinstructions section which actually gets freed. (2) The calls to aarch64_insn_{read,write} bring an awful lot more code into the patching path (e.g. taking locks, poking the fixmap, invalidating the TLB) which isn't actually needed for the early patching run under stop_machine, but makes the use of alternative sequences extremely fragile (as we can't patch code that could be used by the patching code). Given that no code actually requires alternative patching of immediate branches, let's remove this support for now and revisit it when we've got a user. We leave the updated size check, since we really do require the sequences to be the same length. Acked-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
Diffstat (limited to 'arch/arm64')
-rw-r--r--arch/arm64/kernel/alternative.c53
1 files changed, 1 insertions, 52 deletions
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 21033bba9390..28f8365edc4c 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -24,7 +24,6 @@
24#include <asm/cacheflush.h> 24#include <asm/cacheflush.h>
25#include <asm/alternative.h> 25#include <asm/alternative.h>
26#include <asm/cpufeature.h> 26#include <asm/cpufeature.h>
27#include <asm/insn.h>
28#include <linux/stop_machine.h> 27#include <linux/stop_machine.h>
29 28
30extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 29extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
@@ -34,48 +33,6 @@ struct alt_region {
34 struct alt_instr *end; 33 struct alt_instr *end;
35}; 34};
36 35
37/*
38 * Decode the imm field of a b/bl instruction, and return the byte
39 * offset as a signed value (so it can be used when computing a new
40 * branch target).
41 */
42static s32 get_branch_offset(u32 insn)
43{
44 s32 imm = aarch64_insn_decode_immediate(AARCH64_INSN_IMM_26, insn);
45
46 /* sign-extend the immediate before turning it into a byte offset */
47 return (imm << 6) >> 4;
48}
49
50static u32 get_alt_insn(u8 *insnptr, u8 *altinsnptr)
51{
52 u32 insn;
53
54 aarch64_insn_read(altinsnptr, &insn);
55
56 /* Stop the world on instructions we don't support... */
57 BUG_ON(aarch64_insn_is_cbz(insn));
58 BUG_ON(aarch64_insn_is_cbnz(insn));
59 BUG_ON(aarch64_insn_is_bcond(insn));
60 /* ... and there is probably more. */
61
62 if (aarch64_insn_is_b(insn) || aarch64_insn_is_bl(insn)) {
63 enum aarch64_insn_branch_type type;
64 unsigned long target;
65
66 if (aarch64_insn_is_b(insn))
67 type = AARCH64_INSN_BRANCH_NOLINK;
68 else
69 type = AARCH64_INSN_BRANCH_LINK;
70
71 target = (unsigned long)altinsnptr + get_branch_offset(insn);
72 insn = aarch64_insn_gen_branch_imm((unsigned long)insnptr,
73 target, type);
74 }
75
76 return insn;
77}
78
79static int __apply_alternatives(void *alt_region) 36static int __apply_alternatives(void *alt_region)
80{ 37{
81 struct alt_instr *alt; 38 struct alt_instr *alt;
@@ -83,9 +40,6 @@ static int __apply_alternatives(void *alt_region)
83 u8 *origptr, *replptr; 40 u8 *origptr, *replptr;
84 41
85 for (alt = region->begin; alt < region->end; alt++) { 42 for (alt = region->begin; alt < region->end; alt++) {
86 u32 insn;
87 int i;
88
89 if (!cpus_have_cap(alt->cpufeature)) 43 if (!cpus_have_cap(alt->cpufeature))
90 continue; 44 continue;
91 45
@@ -95,12 +49,7 @@ static int __apply_alternatives(void *alt_region)
95 49
96 origptr = (u8 *)&alt->orig_offset + alt->orig_offset; 50 origptr = (u8 *)&alt->orig_offset + alt->orig_offset;
97 replptr = (u8 *)&alt->alt_offset + alt->alt_offset; 51 replptr = (u8 *)&alt->alt_offset + alt->alt_offset;
98 52 memcpy(origptr, replptr, alt->alt_len);
99 for (i = 0; i < alt->alt_len; i += sizeof(insn)) {
100 insn = get_alt_insn(origptr + i, replptr + i);
101 aarch64_insn_write(origptr + i, insn);
102 }
103
104 flush_icache_range((uintptr_t)origptr, 53 flush_icache_range((uintptr_t)origptr,
105 (uintptr_t)(origptr + alt->alt_len)); 54 (uintptr_t)(origptr + alt->alt_len));
106 } 55 }