arm64: add module support for alternatives fixups
authorAndre Przywara <andre.przywara@arm.com>
Fri, 28 Nov 2014 13:40:45 +0000 (13:40 +0000)
committerWill Deacon <will.deacon@arm.com>
Thu, 4 Dec 2014 10:28:24 +0000 (10:28 +0000)
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>
arch/arm64/include/asm/alternative.h
arch/arm64/kernel/alternative.c
arch/arm64/kernel/module.c
arch/arm64/kernel/smp.c

index f6d206e7f9e96b3b8e99339159337c351f92f2de..d261f01e2baeee70c53f3ac7a6f44b7b6e21a98f 100644 (file)
@@ -13,7 +13,8 @@ struct alt_instr {
        u8  alt_len;            /* size of new instruction(s), <= orig_len */
 };
 
-void apply_alternatives(void);
+void apply_alternatives_all(void);
+void apply_alternatives(void *start, size_t length);
 void free_alternatives_memory(void);
 
 #define ALTINSTR_ENTRY(feature)                                                      \
index 1a3badab800a1c513b4bbe9058fe421f4c100949..ad7821d64a1d156fa05cf6b09932054ab4ab037b 100644 (file)
 
 extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
 
-static int __apply_alternatives(void *dummy)
+struct alt_region {
+       struct alt_instr *begin;
+       struct alt_instr *end;
+};
+
+static int __apply_alternatives(void *alt_region)
 {
        struct alt_instr *alt;
+       struct alt_region *region = alt_region;
        u8 *origptr, *replptr;
 
-       for (alt = __alt_instructions; alt < __alt_instructions_end; alt++) {
+       for (alt = region->begin; alt < region->end; alt++) {
                if (!cpus_have_cap(alt->cpufeature))
                        continue;
 
@@ -51,10 +57,25 @@ static int __apply_alternatives(void *dummy)
        return 0;
 }
 
-void apply_alternatives(void)
+void apply_alternatives_all(void)
 {
+       struct alt_region region = {
+               .begin  = __alt_instructions,
+               .end    = __alt_instructions_end,
+       };
+
        /* better not try code patching on a live SMP system */
-       stop_machine(__apply_alternatives, NULL, NULL);
+       stop_machine(__apply_alternatives, &region, NULL);
+}
+
+void apply_alternatives(void *start, size_t length)
+{
+       struct alt_region region = {
+               .begin  = start,
+               .end    = start + length,
+       };
+
+       __apply_alternatives(&region);
 }
 
 void free_alternatives_memory(void)
index 1eb1cc955139537e1b66cbdc78accd5830f0ca06..fd027b101de59fd350ed45d2ec928693e4b9be41 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/moduleloader.h>
 #include <linux/vmalloc.h>
 #include <asm/insn.h>
+#include <asm/sections.h>
 
 #define        AARCH64_INSN_IMM_MOVNZ          AARCH64_INSN_IMM_MAX
 #define        AARCH64_INSN_IMM_MOVK           AARCH64_INSN_IMM_16
@@ -394,3 +395,20 @@ overflow:
               me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
        return -ENOEXEC;
 }
+
+int module_finalize(const Elf_Ehdr *hdr,
+                   const Elf_Shdr *sechdrs,
+                   struct module *me)
+{
+       const Elf_Shdr *s, *se;
+       const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
+
+       for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
+               if (strcmp(".altinstructions", secstrs + s->sh_name) == 0) {
+                       apply_alternatives((void *)s->sh_addr, s->sh_size);
+                       return 0;
+               }
+       }
+
+       return 0;
+}
index 0ef87896e4ae72f9c2f1db19c143c095bdb79fdb..7ae6ee0852618e7c383dfd8d06626297788ac59a 100644 (file)
@@ -310,7 +310,7 @@ void cpu_die(void)
 void __init smp_cpus_done(unsigned int max_cpus)
 {
        pr_info("SMP: Total of %d processors activated.\n", num_online_cpus());
-       apply_alternatives();
+       apply_alternatives_all();
 }
 
 void __init smp_prepare_boot_cpu(void)