aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndre Przywara <andre.przywara@arm.com>2014-11-28 08:40:45 -0500
committerWill Deacon <will.deacon@arm.com>2014-12-04 05:28:24 -0500
commit932ded4b0b9bf111fbf9d176ec12152a0d29b0fd (patch)
tree72ae4cdbea21713d55576bf12fa8c095d3b48679
parentcbbf2e6ed7c2adabfa5cc64901c7b89e029d1e20 (diff)
arm64: add module support for alternatives fixups
Currently the kernel patches all necessary instructions once at boot time, so modules are not covered by this. Change the apply_alternatives() function to take a beginning and an end pointer and introduce a new variant (apply_alternatives_all()) to cover the existing use case for the static kernel image section. Add a module_finalize() function to arm64 to check for an alternatives section in a module and patch only the instructions from that specific area. Since that module code is not touched before the module initialization has ended, we don't need to halt the machine before doing the patching in the module's code. Signed-off-by: Andre Przywara <andre.przywara@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
-rw-r--r--arch/arm64/include/asm/alternative.h3
-rw-r--r--arch/arm64/kernel/alternative.c29
-rw-r--r--arch/arm64/kernel/module.c18
-rw-r--r--arch/arm64/kernel/smp.c2
4 files changed, 46 insertions, 6 deletions
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
index f6d206e7f9e9..d261f01e2bae 100644
--- a/arch/arm64/include/asm/alternative.h
+++ b/arch/arm64/include/asm/alternative.h
@@ -13,7 +13,8 @@ struct alt_instr {
13 u8 alt_len; /* size of new instruction(s), <= orig_len */ 13 u8 alt_len; /* size of new instruction(s), <= orig_len */
14}; 14};
15 15
16void apply_alternatives(void); 16void apply_alternatives_all(void);
17void apply_alternatives(void *start, size_t length);
17void free_alternatives_memory(void); 18void free_alternatives_memory(void);
18 19
19#define ALTINSTR_ENTRY(feature) \ 20#define ALTINSTR_ENTRY(feature) \
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
index 1a3badab800a..ad7821d64a1d 100644
--- a/arch/arm64/kernel/alternative.c
+++ b/arch/arm64/kernel/alternative.c
@@ -28,12 +28,18 @@
28 28
29extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; 29extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
30 30
31static int __apply_alternatives(void *dummy) 31struct alt_region {
32 struct alt_instr *begin;
33 struct alt_instr *end;
34};
35
36static int __apply_alternatives(void *alt_region)
32{ 37{
33 struct alt_instr *alt; 38 struct alt_instr *alt;
39 struct alt_region *region = alt_region;
34 u8 *origptr, *replptr; 40 u8 *origptr, *replptr;
35 41
36 for (alt = __alt_instructions; alt < __alt_instructions_end; alt++) { 42 for (alt = region->begin; alt < region->end; alt++) {
37 if (!cpus_have_cap(alt->cpufeature)) 43 if (!cpus_have_cap(alt->cpufeature))
38 continue; 44 continue;
39 45
@@ -51,10 +57,25 @@ static int __apply_alternatives(void *dummy)
51 return 0; 57 return 0;
52} 58}
53 59
54void apply_alternatives(void) 60void apply_alternatives_all(void)
55{ 61{
62 struct alt_region region = {
63 .begin = __alt_instructions,
64 .end = __alt_instructions_end,
65 };
66
56 /* better not try code patching on a live SMP system */ 67 /* better not try code patching on a live SMP system */
57 stop_machine(__apply_alternatives, NULL, NULL); 68 stop_machine(__apply_alternatives, &region, NULL);
69}
70
71void apply_alternatives(void *start, size_t length)
72{
73 struct alt_region region = {
74 .begin = start,
75 .end = start + length,
76 };
77
78 __apply_alternatives(&region);
58} 79}
59 80
60void free_alternatives_memory(void) 81void free_alternatives_memory(void)
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index 1eb1cc955139..fd027b101de5 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -26,6 +26,7 @@
26#include <linux/moduleloader.h> 26#include <linux/moduleloader.h>
27#include <linux/vmalloc.h> 27#include <linux/vmalloc.h>
28#include <asm/insn.h> 28#include <asm/insn.h>
29#include <asm/sections.h>
29 30
30#define AARCH64_INSN_IMM_MOVNZ AARCH64_INSN_IMM_MAX 31#define AARCH64_INSN_IMM_MOVNZ AARCH64_INSN_IMM_MAX
31#define AARCH64_INSN_IMM_MOVK AARCH64_INSN_IMM_16 32#define AARCH64_INSN_IMM_MOVK AARCH64_INSN_IMM_16
@@ -394,3 +395,20 @@ overflow:
394 me->name, (int)ELF64_R_TYPE(rel[i].r_info), val); 395 me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
395 return -ENOEXEC; 396 return -ENOEXEC;
396} 397}
398
399int module_finalize(const Elf_Ehdr *hdr,
400 const Elf_Shdr *sechdrs,
401 struct module *me)
402{
403 const Elf_Shdr *s, *se;
404 const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
405
406 for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
407 if (strcmp(".altinstructions", secstrs + s->sh_name) == 0) {
408 apply_alternatives((void *)s->sh_addr, s->sh_size);
409 return 0;
410 }
411 }
412
413 return 0;
414}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 0ef87896e4ae..7ae6ee085261 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -310,7 +310,7 @@ void cpu_die(void)
310void __init smp_cpus_done(unsigned int max_cpus) 310void __init smp_cpus_done(unsigned int max_cpus)
311{ 311{
312 pr_info("SMP: Total of %d processors activated.\n", num_online_cpus()); 312 pr_info("SMP: Total of %d processors activated.\n", num_online_cpus());
313 apply_alternatives(); 313 apply_alternatives_all();
314} 314}
315 315
316void __init smp_prepare_boot_cpu(void) 316void __init smp_prepare_boot_cpu(void)