diff options
Diffstat (limited to 'arch/s390/kernel/kprobes.c')
-rw-r--r-- | arch/s390/kernel/kprobes.c | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index b2e1dc89a8c6..a466bab6677e 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
@@ -155,15 +155,34 @@ void __kprobes get_instruction_type(struct arch_specific_insn *ainsn) | |||
155 | static int __kprobes swap_instruction(void *aref) | 155 | static int __kprobes swap_instruction(void *aref) |
156 | { | 156 | { |
157 | struct ins_replace_args *args = aref; | 157 | struct ins_replace_args *args = aref; |
158 | u32 *addr; | ||
159 | u32 instr; | ||
158 | int err = -EFAULT; | 160 | int err = -EFAULT; |
159 | 161 | ||
162 | /* | ||
163 | * Text segment is read-only, hence we use stura to bypass dynamic | ||
164 | * address translation to exchange the instruction. Since stura | ||
165 | * always operates on four bytes, but we only want to exchange two | ||
166 | * bytes do some calculations to get things right. In addition we | ||
167 | * shall not cross any page boundaries (vmalloc area!) when writing | ||
168 | * the new instruction. | ||
169 | */ | ||
170 | addr = (u32 *)ALIGN((unsigned long)args->ptr, 4); | ||
171 | if ((unsigned long)args->ptr & 2) | ||
172 | instr = ((*addr) & 0xffff0000) | args->new; | ||
173 | else | ||
174 | instr = ((*addr) & 0x0000ffff) | args->new << 16; | ||
175 | |||
160 | asm volatile( | 176 | asm volatile( |
161 | "0: mvc 0(2,%2),0(%3)\n" | 177 | " lra %1,0(%1)\n" |
162 | "1: la %0,0\n" | 178 | "0: stura %2,%1\n" |
179 | "1: la %0,0\n" | ||
163 | "2:\n" | 180 | "2:\n" |
164 | EX_TABLE(0b,2b) | 181 | EX_TABLE(0b,2b) |
165 | : "+d" (err), "=m" (*args->ptr) | 182 | : "+d" (err) |
166 | : "a" (args->ptr), "a" (&args->new), "m" (args->new)); | 183 | : "a" (addr), "d" (instr) |
184 | : "memory", "cc"); | ||
185 | |||
167 | return err; | 186 | return err; |
168 | } | 187 | } |
169 | 188 | ||