aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/include/asm/alternative-asm.h16
-rw-r--r--arch/arm64/include/asm/alternative.h43
-rw-r--r--arch/arm64/kernel/Makefile2
-rw-r--r--arch/arm64/kernel/alternative.c64
-rw-r--r--arch/arm64/kernel/smp.c2
-rw-r--r--arch/arm64/kernel/vmlinux.lds.S11
-rw-r--r--arch/arm64/mm/init.c2
7 files changed, 139 insertions, 1 deletions
diff --git a/arch/arm64/include/asm/alternative-asm.h b/arch/arm64/include/asm/alternative-asm.h
new file mode 100644
index 000000000000..5ee9340459b8
--- /dev/null
+++ b/arch/arm64/include/asm/alternative-asm.h
@@ -0,0 +1,16 @@
1#ifndef __ASM_ALTERNATIVE_ASM_H
2#define __ASM_ALTERNATIVE_ASM_H
3
4#ifdef __ASSEMBLY__
5
6.macro altinstruction_entry orig_offset alt_offset feature orig_len alt_len
7 .word \orig_offset - .
8 .word \alt_offset - .
9 .hword \feature
10 .byte \orig_len
11 .byte \alt_len
12.endm
13
14#endif /* __ASSEMBLY__ */
15
16#endif /* __ASM_ALTERNATIVE_ASM_H */
diff --git a/arch/arm64/include/asm/alternative.h b/arch/arm64/include/asm/alternative.h
new file mode 100644
index 000000000000..f6d206e7f9e9
--- /dev/null
+++ b/arch/arm64/include/asm/alternative.h
@@ -0,0 +1,43 @@
1#ifndef __ASM_ALTERNATIVE_H
2#define __ASM_ALTERNATIVE_H
3
4#include <linux/types.h>
5#include <linux/stddef.h>
6#include <linux/stringify.h>
7
8struct alt_instr {
9 s32 orig_offset; /* offset to original instruction */
10 s32 alt_offset; /* offset to replacement instruction */
11 u16 cpufeature; /* cpufeature bit set for replacement */
12 u8 orig_len; /* size of original instruction(s) */
13 u8 alt_len; /* size of new instruction(s), <= orig_len */
14};
15
16void apply_alternatives(void);
17void free_alternatives_memory(void);
18
19#define ALTINSTR_ENTRY(feature) \
20 " .word 661b - .\n" /* label */ \
21 " .word 663f - .\n" /* new instruction */ \
22 " .hword " __stringify(feature) "\n" /* feature bit */ \
23 " .byte 662b-661b\n" /* source len */ \
24 " .byte 664f-663f\n" /* replacement len */
25
26/* alternative assembly primitive: */
27#define ALTERNATIVE(oldinstr, newinstr, feature) \
28 "661:\n\t" \
29 oldinstr "\n" \
30 "662:\n" \
31 ".pushsection .altinstructions,\"a\"\n" \
32 ALTINSTR_ENTRY(feature) \
33 ".popsection\n" \
34 ".pushsection .altinstr_replacement, \"a\"\n" \
35 "663:\n\t" \
36 newinstr "\n" \
37 "664:\n\t" \
38 ".popsection\n\t" \
39 ".if ((664b-663b) != (662b-661b))\n\t" \
40 " .error \"Alternatives instruction length mismatch\"\n\t"\
41 ".endif\n"
42
43#endif /* __ASM_ALTERNATIVE_H */
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
index b36ebd0aacbb..591a65dc5c9b 100644
--- a/arch/arm64/kernel/Makefile
+++ b/arch/arm64/kernel/Makefile
@@ -16,7 +16,7 @@ arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
16 entry-fpsimd.o process.o ptrace.o setup.o signal.o \ 16 entry-fpsimd.o process.o ptrace.o setup.o signal.o \
17 sys.o stacktrace.o time.o traps.o io.o vdso.o \ 17 sys.o stacktrace.o time.o traps.o io.o vdso.o \
18 hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \ 18 hyp-stub.o psci.o cpu_ops.o insn.o return_address.o \
19 cpuinfo.o 19 cpuinfo.o alternative.o
20 20
21arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \ 21arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
22 sys_compat.o \ 22 sys_compat.o \
diff --git a/arch/arm64/kernel/alternative.c b/arch/arm64/kernel/alternative.c
new file mode 100644
index 000000000000..1a3badab800a
--- /dev/null
+++ b/arch/arm64/kernel/alternative.c
@@ -0,0 +1,64 @@
1/*
2 * alternative runtime patching
3 * inspired by the x86 version
4 *
5 * Copyright (C) 2014 ARM Ltd.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#define pr_fmt(fmt) "alternatives: " fmt
21
22#include <linux/init.h>
23#include <linux/cpu.h>
24#include <asm/cacheflush.h>
25#include <asm/alternative.h>
26#include <asm/cpufeature.h>
27#include <linux/stop_machine.h>
28
29extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
30
31static int __apply_alternatives(void *dummy)
32{
33 struct alt_instr *alt;
34 u8 *origptr, *replptr;
35
36 for (alt = __alt_instructions; alt < __alt_instructions_end; alt++) {
37 if (!cpus_have_cap(alt->cpufeature))
38 continue;
39
40 BUG_ON(alt->alt_len > alt->orig_len);
41
42 pr_info_once("patching kernel code\n");
43
44 origptr = (u8 *)&alt->orig_offset + alt->orig_offset;
45 replptr = (u8 *)&alt->alt_offset + alt->alt_offset;
46 memcpy(origptr, replptr, alt->alt_len);
47 flush_icache_range((uintptr_t)origptr,
48 (uintptr_t)(origptr + alt->alt_len));
49 }
50
51 return 0;
52}
53
54void apply_alternatives(void)
55{
56 /* better not try code patching on a live SMP system */
57 stop_machine(__apply_alternatives, NULL, NULL);
58}
59
60void free_alternatives_memory(void)
61{
62 free_reserved_area(__alt_instructions, __alt_instructions_end,
63 0, "alternatives");
64}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index b06d1d90ee8c..0ef87896e4ae 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -37,6 +37,7 @@
37#include <linux/of.h> 37#include <linux/of.h>
38#include <linux/irq_work.h> 38#include <linux/irq_work.h>
39 39
40#include <asm/alternative.h>
40#include <asm/atomic.h> 41#include <asm/atomic.h>
41#include <asm/cacheflush.h> 42#include <asm/cacheflush.h>
42#include <asm/cpu.h> 43#include <asm/cpu.h>
@@ -309,6 +310,7 @@ void cpu_die(void)
309void __init smp_cpus_done(unsigned int max_cpus) 310void __init smp_cpus_done(unsigned int max_cpus)
310{ 311{
311 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();
312} 314}
313 315
314void __init smp_prepare_boot_cpu(void) 316void __init smp_prepare_boot_cpu(void)
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index 4596f46d0244..3236727be2b9 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -116,6 +116,17 @@ SECTIONS
116 . = ALIGN(PAGE_SIZE); 116 . = ALIGN(PAGE_SIZE);
117 __init_end = .; 117 __init_end = .;
118 118
119 . = ALIGN(4);
120 .altinstructions : {
121 __alt_instructions = .;
122 *(.altinstructions)
123 __alt_instructions_end = .;
124 }
125 .altinstr_replacement : {
126 *(.altinstr_replacement)
127 }
128
129 . = ALIGN(PAGE_SIZE);
119 _data = .; 130 _data = .;
120 _sdata = .; 131 _sdata = .;
121 RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE) 132 RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE)
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 494297c698ca..bac492c12fcc 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -39,6 +39,7 @@
39#include <asm/setup.h> 39#include <asm/setup.h>
40#include <asm/sizes.h> 40#include <asm/sizes.h>
41#include <asm/tlb.h> 41#include <asm/tlb.h>
42#include <asm/alternative.h>
42 43
43#include "mm.h" 44#include "mm.h"
44 45
@@ -325,6 +326,7 @@ void __init mem_init(void)
325void free_initmem(void) 326void free_initmem(void)
326{ 327{
327 free_initmem_default(0); 328 free_initmem_default(0);
329 free_alternatives_memory();
328} 330}
329 331
330#ifdef CONFIG_BLK_DEV_INITRD 332#ifdef CONFIG_BLK_DEV_INITRD