aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2018-06-22 04:31:15 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2018-06-27 13:21:53 -0400
commit429388682dc266e7a693f9c27e3aabd341d55343 (patch)
treeadab7d6b539fcbddbe26923454597ca3dfd803d5
parent7838306f5ace5183086ad507c58f5988913690e4 (diff)
arm64: Avoid flush_icache_range() in alternatives patching code
The implementation of flush_icache_range() includes instruction sequences which are themselves patched at runtime, so it is not safe to call from the patching framework. This patch reworks the alternatives cache-flushing code so that it rolls its own internal D-cache maintenance using DC CIVAC before invalidating the entire I-cache after all alternatives have been applied at boot. Modules don't cause any issues, since flush_icache_range() is safe to call by the time they are loaded. Acked-by: Mark Rutland <mark.rutland@arm.com> Reported-by: Rohit Khanna <rokhanna@nvidia.com> Cc: Alexander Van Brunt <avanbrunt@nvidia.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--arch/arm64/include/asm/alternative.h7
-rw-r--r--arch/arm64/kernel/alternative.c51
-rw-r--r--arch/arm64/kernel/module.c5
3 files changed, 52 insertions, 11 deletions
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index a91933b1e2e6..4b650ec1d7dd 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -28,7 +28,12 @@ typedef void (*alternative_cb_t)(struct alt_instr *alt,
28 __le32 *origptr, __le32 *updptr, int nr_inst); 28 __le32 *origptr, __le32 *updptr, int nr_inst);
29 29
30void __init apply_alternatives_all(void); 30void __init apply_alternatives_all(void);
31void apply_alternatives(void *start, size_t length); 31
32#ifdef CONFIG_MODULES
33void apply_alternatives_module(void *start, size_t length);
34#else
35static inline void apply_alternatives_module(void *start, size_t length) { }
36#endif
32 37
33#define ALTINSTR_ENTRY(feature,cb) \ 38#define ALTINSTR_ENTRY(feature,cb) \
34 " .word 661b - .\n" /* label */ \ 39 " .word 661b - .\n" /* label */ \
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 5c4bce4ac381..36fb069fd049 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -122,7 +122,30 @@ static void patch_alternative(struct alt_instr *alt,
122 } 122 }
123} 123}
124 124
125static void __apply_alternatives(void *alt_region, bool use_linear_alias) 125/*
126 * We provide our own, private D-cache cleaning function so that we don't
127 * accidentally call into the cache.S code, which is patched by us at
128 * runtime.
129 */
130static void clean_dcache_range_nopatch(u64 start, u64 end)
131{
132 u64 cur, d_size, ctr_el0;
133
134 ctr_el0 = read_sanitised_ftr_reg(SYS_CTR_EL0);
135 d_size = 4 << cpuid_feature_extract_unsigned_field(ctr_el0,
136 CTR_DMINLINE_SHIFT);
137 cur = start & ~(d_size - 1);
138 do {
139 /*
140 * We must clean+invalidate to the PoC in order to avoid
141 * Cortex-A53 errata 826319, 827319, 824069 and 819472
142 * (this corresponds to ARM64_WORKAROUND_CLEAN_CACHE)
143 */
144 asm volatile("dc civac, %0" : : "r" (cur) : "memory");
145 } while (cur += d_size, cur < end);
146}
147
148static void __apply_alternatives(void *alt_region, bool is_module)
126{ 149{
127 struct alt_instr *alt; 150 struct alt_instr *alt;
128 struct alt_region *region = alt_region; 151 struct alt_region *region = alt_region;
@@ -145,7 +168,7 @@ static void __apply_alternatives(void *alt_region, bool use_linear_alias)
145 pr_info_once("patching kernel code\n"); 168 pr_info_once("patching kernel code\n");
146 169
147 origptr = ALT_ORIG_PTR(alt); 170 origptr = ALT_ORIG_PTR(alt);
148 updptr = use_linear_alias ? lm_alias(origptr) : origptr; 171 updptr = is_module ? origptr : lm_alias(origptr);
149 nr_inst = alt->orig_len / AARCH64_INSN_SIZE; 172 nr_inst = alt->orig_len / AARCH64_INSN_SIZE;
150 173
151 if (alt->cpufeature < ARM64_CB_PATCH) 174 if (alt->cpufeature < ARM64_CB_PATCH)
@@ -155,8 +178,20 @@ static void __apply_alternatives(void *alt_region, bool use_linear_alias)
155 178
156 alt_cb(alt, origptr, updptr, nr_inst); 179 alt_cb(alt, origptr, updptr, nr_inst);
157 180
158 flush_icache_range((uintptr_t)origptr, 181 if (!is_module) {
159 (uintptr_t)(origptr + nr_inst)); 182 clean_dcache_range_nopatch((u64)origptr,
183 (u64)(origptr + nr_inst));
184 }
185 }
186
187 /*
188 * The core module code takes care of cache maintenance in
189 * flush_module_icache().
190 */
191 if (!is_module) {
192 dsb(ish);
193 __flush_icache_all();
194 isb();
160 } 195 }
161} 196}
162 197
@@ -178,7 +213,7 @@ static int __apply_alternatives_multi_stop(void *unused)
178 isb(); 213 isb();
179 } else { 214 } else {
180 BUG_ON(alternatives_applied); 215 BUG_ON(alternatives_applied);
181 __apply_alternatives(&region, true); 216 __apply_alternatives(&region, false);
182 /* Barriers provided by the cache flushing */ 217 /* Barriers provided by the cache flushing */
183 WRITE_ONCE(alternatives_applied, 1); 218 WRITE_ONCE(alternatives_applied, 1);
184 } 219 }
@@ -192,12 +227,14 @@ void __init apply_alternatives_all(void)
192 stop_machine(__apply_alternatives_multi_stop, NULL, cpu_online_mask); 227 stop_machine(__apply_alternatives_multi_stop, NULL, cpu_online_mask);
193} 228}
194 229
195void apply_alternatives(void *start, size_t length) 230#ifdef CONFIG_MODULES
231void apply_alternatives_module(void *start, size_t length)
196{ 232{
197 struct alt_region region = { 233 struct alt_region region = {
198 .begin = start, 234 .begin = start,
199 .end = start + length, 235 .end = start + length,
200 }; 236 };
201 237
202 __apply_alternatives(&region, false); 238 __apply_alternatives(&region, true);
203} 239}
240#endif
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 155fd91e78f4..f0f27aeefb73 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -448,9 +448,8 @@ int module_finalize(const Elf_Ehdr *hdr,
448 const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; 448 const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
449 449
450 for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) { 450 for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
451 if (strcmp(".altinstructions", secstrs + s->sh_name) == 0) { 451 if (strcmp(".altinstructions", secstrs + s->sh_name) == 0)
452 apply_alternatives((void *)s->sh_addr, s->sh_size); 452 apply_alternatives_module((void *)s->sh_addr, s->sh_size);
453 }
454#ifdef CONFIG_ARM64_MODULE_PLTS 453#ifdef CONFIG_ARM64_MODULE_PLTS
455 if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE) && 454 if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE) &&
456 !strcmp(".text.ftrace_trampoline", secstrs + s->sh_name)) 455 !strcmp(".text.ftrace_trampoline", secstrs + s->sh_name))