diff options
Diffstat (limited to 'arch/powerpc/lib/code-patching.c')
-rw-r--r-- | arch/powerpc/lib/code-patching.c | 37 |
1 files changed, 21 insertions, 16 deletions
diff --git a/arch/powerpc/lib/code-patching.c b/arch/powerpc/lib/code-patching.c index d469224c4ada..e0d881ab304e 100644 --- a/arch/powerpc/lib/code-patching.c +++ b/arch/powerpc/lib/code-patching.c | |||
@@ -23,19 +23,26 @@ | |||
23 | #include <asm/code-patching.h> | 23 | #include <asm/code-patching.h> |
24 | #include <asm/setup.h> | 24 | #include <asm/setup.h> |
25 | 25 | ||
26 | static int __patch_instruction(unsigned int *addr, unsigned int instr) | 26 | static int __patch_instruction(unsigned int *exec_addr, unsigned int instr, |
27 | unsigned int *patch_addr) | ||
27 | { | 28 | { |
28 | int err; | 29 | int err; |
29 | 30 | ||
30 | __put_user_size(instr, addr, 4, err); | 31 | __put_user_size(instr, patch_addr, 4, err); |
31 | if (err) | 32 | if (err) |
32 | return err; | 33 | return err; |
33 | 34 | ||
34 | asm ("dcbst 0, %0; sync; icbi 0,%0; sync; isync" :: "r" (addr)); | 35 | asm ("dcbst 0, %0; sync; icbi 0,%1; sync; isync" :: "r" (patch_addr), |
36 | "r" (exec_addr)); | ||
35 | 37 | ||
36 | return 0; | 38 | return 0; |
37 | } | 39 | } |
38 | 40 | ||
41 | int raw_patch_instruction(unsigned int *addr, unsigned int instr) | ||
42 | { | ||
43 | return __patch_instruction(addr, instr, addr); | ||
44 | } | ||
45 | |||
39 | #ifdef CONFIG_STRICT_KERNEL_RWX | 46 | #ifdef CONFIG_STRICT_KERNEL_RWX |
40 | static DEFINE_PER_CPU(struct vm_struct *, text_poke_area); | 47 | static DEFINE_PER_CPU(struct vm_struct *, text_poke_area); |
41 | 48 | ||
@@ -138,7 +145,7 @@ static inline int unmap_patch_area(unsigned long addr) | |||
138 | int patch_instruction(unsigned int *addr, unsigned int instr) | 145 | int patch_instruction(unsigned int *addr, unsigned int instr) |
139 | { | 146 | { |
140 | int err; | 147 | int err; |
141 | unsigned int *dest = NULL; | 148 | unsigned int *patch_addr = NULL; |
142 | unsigned long flags; | 149 | unsigned long flags; |
143 | unsigned long text_poke_addr; | 150 | unsigned long text_poke_addr; |
144 | unsigned long kaddr = (unsigned long)addr; | 151 | unsigned long kaddr = (unsigned long)addr; |
@@ -148,8 +155,8 @@ int patch_instruction(unsigned int *addr, unsigned int instr) | |||
148 | * when text_poke_area is not ready, but we still need | 155 | * when text_poke_area is not ready, but we still need |
149 | * to allow patching. We just do the plain old patching | 156 | * to allow patching. We just do the plain old patching |
150 | */ | 157 | */ |
151 | if (!this_cpu_read(*PTRRELOC(&text_poke_area))) | 158 | if (!this_cpu_read(text_poke_area)) |
152 | return __patch_instruction(addr, instr); | 159 | return raw_patch_instruction(addr, instr); |
153 | 160 | ||
154 | local_irq_save(flags); | 161 | local_irq_save(flags); |
155 | 162 | ||
@@ -159,17 +166,10 @@ int patch_instruction(unsigned int *addr, unsigned int instr) | |||
159 | goto out; | 166 | goto out; |
160 | } | 167 | } |
161 | 168 | ||
162 | dest = (unsigned int *)(text_poke_addr) + | 169 | patch_addr = (unsigned int *)(text_poke_addr) + |
163 | ((kaddr & ~PAGE_MASK) / sizeof(unsigned int)); | 170 | ((kaddr & ~PAGE_MASK) / sizeof(unsigned int)); |
164 | 171 | ||
165 | /* | 172 | __patch_instruction(addr, instr, patch_addr); |
166 | * We use __put_user_size so that we can handle faults while | ||
167 | * writing to dest and return err to handle faults gracefully | ||
168 | */ | ||
169 | __put_user_size(instr, dest, 4, err); | ||
170 | if (!err) | ||
171 | asm ("dcbst 0, %0; sync; icbi 0,%0; icbi 0,%1; sync; isync" | ||
172 | ::"r" (dest), "r"(addr)); | ||
173 | 173 | ||
174 | err = unmap_patch_area(text_poke_addr); | 174 | err = unmap_patch_area(text_poke_addr); |
175 | if (err) | 175 | if (err) |
@@ -184,7 +184,7 @@ out: | |||
184 | 184 | ||
185 | int patch_instruction(unsigned int *addr, unsigned int instr) | 185 | int patch_instruction(unsigned int *addr, unsigned int instr) |
186 | { | 186 | { |
187 | return __patch_instruction(addr, instr); | 187 | return raw_patch_instruction(addr, instr); |
188 | } | 188 | } |
189 | 189 | ||
190 | #endif /* CONFIG_STRICT_KERNEL_RWX */ | 190 | #endif /* CONFIG_STRICT_KERNEL_RWX */ |
@@ -302,6 +302,11 @@ int instr_is_relative_branch(unsigned int instr) | |||
302 | return instr_is_branch_iform(instr) || instr_is_branch_bform(instr); | 302 | return instr_is_branch_iform(instr) || instr_is_branch_bform(instr); |
303 | } | 303 | } |
304 | 304 | ||
305 | int instr_is_relative_link_branch(unsigned int instr) | ||
306 | { | ||
307 | return instr_is_relative_branch(instr) && (instr & BRANCH_SET_LINK); | ||
308 | } | ||
309 | |||
305 | static unsigned long branch_iform_target(const unsigned int *instr) | 310 | static unsigned long branch_iform_target(const unsigned int *instr) |
306 | { | 311 | { |
307 | signed long imm; | 312 | signed long imm; |