]>
Commit | Line | Data |
---|---|---|
e039ee4e AP |
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 | ||
29 | extern struct alt_instr __alt_instructions[], __alt_instructions_end[]; | |
30 | ||
932ded4b AP |
31 | struct alt_region { |
32 | struct alt_instr *begin; | |
33 | struct alt_instr *end; | |
34 | }; | |
35 | ||
36 | static int __apply_alternatives(void *alt_region) | |
e039ee4e AP |
37 | { |
38 | struct alt_instr *alt; | |
932ded4b | 39 | struct alt_region *region = alt_region; |
e039ee4e AP |
40 | u8 *origptr, *replptr; |
41 | ||
932ded4b | 42 | for (alt = region->begin; alt < region->end; alt++) { |
e039ee4e AP |
43 | if (!cpus_have_cap(alt->cpufeature)) |
44 | continue; | |
45 | ||
46 | BUG_ON(alt->alt_len > alt->orig_len); | |
47 | ||
48 | pr_info_once("patching kernel code\n"); | |
49 | ||
50 | origptr = (u8 *)&alt->orig_offset + alt->orig_offset; | |
51 | replptr = (u8 *)&alt->alt_offset + alt->alt_offset; | |
52 | memcpy(origptr, replptr, alt->alt_len); | |
53 | flush_icache_range((uintptr_t)origptr, | |
54 | (uintptr_t)(origptr + alt->alt_len)); | |
55 | } | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
932ded4b | 60 | void apply_alternatives_all(void) |
e039ee4e | 61 | { |
932ded4b AP |
62 | struct alt_region region = { |
63 | .begin = __alt_instructions, | |
64 | .end = __alt_instructions_end, | |
65 | }; | |
66 | ||
e039ee4e | 67 | /* better not try code patching on a live SMP system */ |
932ded4b AP |
68 | stop_machine(__apply_alternatives, ®ion, NULL); |
69 | } | |
70 | ||
71 | void 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(®ion); | |
e039ee4e AP |
79 | } |
80 | ||
81 | void free_alternatives_memory(void) | |
82 | { | |
83 | free_reserved_area(__alt_instructions, __alt_instructions_end, | |
84 | 0, "alternatives"); | |
85 | } |