|
@@ -122,7 +122,30 @@ static void patch_alternative(struct alt_instr *alt,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static void __apply_alternatives(void *alt_region, bool use_linear_alias)
|
|
|
+/*
|
|
|
+ * We provide our own, private D-cache cleaning function so that we don't
|
|
|
+ * accidentally call into the cache.S code, which is patched by us at
|
|
|
+ * runtime.
|
|
|
+ */
|
|
|
+static void clean_dcache_range_nopatch(u64 start, u64 end)
|
|
|
+{
|
|
|
+ u64 cur, d_size, ctr_el0;
|
|
|
+
|
|
|
+ ctr_el0 = read_sanitised_ftr_reg(SYS_CTR_EL0);
|
|
|
+ d_size = 4 << cpuid_feature_extract_unsigned_field(ctr_el0,
|
|
|
+ CTR_DMINLINE_SHIFT);
|
|
|
+ cur = start & ~(d_size - 1);
|
|
|
+ do {
|
|
|
+ /*
|
|
|
+ * We must clean+invalidate to the PoC in order to avoid
|
|
|
+ * Cortex-A53 errata 826319, 827319, 824069 and 819472
|
|
|
+ * (this corresponds to ARM64_WORKAROUND_CLEAN_CACHE)
|
|
|
+ */
|
|
|
+ asm volatile("dc civac, %0" : : "r" (cur) : "memory");
|
|
|
+ } while (cur += d_size, cur < end);
|
|
|
+}
|
|
|
+
|
|
|
+static void __apply_alternatives(void *alt_region, bool is_module)
|
|
|
{
|
|
|
struct alt_instr *alt;
|
|
|
struct alt_region *region = alt_region;
|
|
@@ -145,7 +168,7 @@ static void __apply_alternatives(void *alt_region, bool use_linear_alias)
|
|
|
pr_info_once("patching kernel code\n");
|
|
|
|
|
|
origptr = ALT_ORIG_PTR(alt);
|
|
|
- updptr = use_linear_alias ? lm_alias(origptr) : origptr;
|
|
|
+ updptr = is_module ? origptr : lm_alias(origptr);
|
|
|
nr_inst = alt->orig_len / AARCH64_INSN_SIZE;
|
|
|
|
|
|
if (alt->cpufeature < ARM64_CB_PATCH)
|
|
@@ -155,8 +178,20 @@ static void __apply_alternatives(void *alt_region, bool use_linear_alias)
|
|
|
|
|
|
alt_cb(alt, origptr, updptr, nr_inst);
|
|
|
|
|
|
- flush_icache_range((uintptr_t)origptr,
|
|
|
- (uintptr_t)(origptr + nr_inst));
|
|
|
+ if (!is_module) {
|
|
|
+ clean_dcache_range_nopatch((u64)origptr,
|
|
|
+ (u64)(origptr + nr_inst));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The core module code takes care of cache maintenance in
|
|
|
+ * flush_module_icache().
|
|
|
+ */
|
|
|
+ if (!is_module) {
|
|
|
+ dsb(ish);
|
|
|
+ __flush_icache_all();
|
|
|
+ isb();
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -178,7 +213,7 @@ static int __apply_alternatives_multi_stop(void *unused)
|
|
|
isb();
|
|
|
} else {
|
|
|
BUG_ON(alternatives_applied);
|
|
|
- __apply_alternatives(®ion, true);
|
|
|
+ __apply_alternatives(®ion, false);
|
|
|
/* Barriers provided by the cache flushing */
|
|
|
WRITE_ONCE(alternatives_applied, 1);
|
|
|
}
|
|
@@ -192,12 +227,14 @@ void __init apply_alternatives_all(void)
|
|
|
stop_machine(__apply_alternatives_multi_stop, NULL, cpu_online_mask);
|
|
|
}
|
|
|
|
|
|
-void apply_alternatives(void *start, size_t length)
|
|
|
+#ifdef CONFIG_MODULES
|
|
|
+void apply_alternatives_module(void *start, size_t length)
|
|
|
{
|
|
|
struct alt_region region = {
|
|
|
.begin = start,
|
|
|
.end = start + length,
|
|
|
};
|
|
|
|
|
|
- __apply_alternatives(®ion, false);
|
|
|
+ __apply_alternatives(®ion, true);
|
|
|
}
|
|
|
+#endif
|