| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Code for Kernel probes Jump optimization. |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright 2017, Anju T, IBM Corp. |
|---|
| 5 | | - * |
|---|
| 6 | | - * This program is free software; you can redistribute it and/or |
|---|
| 7 | | - * modify it under the terms of the GNU General Public License |
|---|
| 8 | | - * as published by the Free Software Foundation; either version |
|---|
| 9 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 10 | 6 | */ |
|---|
| 11 | 7 | |
|---|
| 12 | 8 | #include <linux/kprobes.h> |
|---|
| .. | .. |
|---|
| 20 | 16 | #include <asm/code-patching.h> |
|---|
| 21 | 17 | #include <asm/sstep.h> |
|---|
| 22 | 18 | #include <asm/ppc-opcode.h> |
|---|
| 19 | +#include <asm/inst.h> |
|---|
| 23 | 20 | |
|---|
| 24 | 21 | #define TMPL_CALL_HDLR_IDX \ |
|---|
| 25 | 22 | (optprobe_template_call_handler - optprobe_template_entry) |
|---|
| .. | .. |
|---|
| 103 | 100 | * Ensure that the instruction is not a conditional branch, |
|---|
| 104 | 101 | * and that can be emulated. |
|---|
| 105 | 102 | */ |
|---|
| 106 | | - if (!is_conditional_branch(*p->ainsn.insn) && |
|---|
| 107 | | - analyse_instr(&op, ®s, *p->ainsn.insn) == 1) { |
|---|
| 103 | + if (!is_conditional_branch(ppc_inst_read((struct ppc_inst *)p->ainsn.insn)) && |
|---|
| 104 | + analyse_instr(&op, ®s, |
|---|
| 105 | + ppc_inst_read((struct ppc_inst *)p->ainsn.insn)) == 1) { |
|---|
| 108 | 106 | emulate_update_regs(®s, &op); |
|---|
| 109 | 107 | nip = regs.nip; |
|---|
| 110 | 108 | } |
|---|
| .. | .. |
|---|
| 151 | 149 | void patch_imm32_load_insns(unsigned int val, kprobe_opcode_t *addr) |
|---|
| 152 | 150 | { |
|---|
| 153 | 151 | /* addis r4,0,(insn)@h */ |
|---|
| 154 | | - patch_instruction(addr, PPC_INST_ADDIS | ___PPC_RT(4) | |
|---|
| 155 | | - ((val >> 16) & 0xffff)); |
|---|
| 152 | + patch_instruction((struct ppc_inst *)addr, |
|---|
| 153 | + ppc_inst(PPC_INST_ADDIS | ___PPC_RT(4) | |
|---|
| 154 | + ((val >> 16) & 0xffff))); |
|---|
| 156 | 155 | addr++; |
|---|
| 157 | 156 | |
|---|
| 158 | 157 | /* ori r4,r4,(insn)@l */ |
|---|
| 159 | | - patch_instruction(addr, PPC_INST_ORI | ___PPC_RA(4) | |
|---|
| 160 | | - ___PPC_RS(4) | (val & 0xffff)); |
|---|
| 158 | + patch_instruction((struct ppc_inst *)addr, |
|---|
| 159 | + ppc_inst(PPC_INST_ORI | ___PPC_RA(4) | |
|---|
| 160 | + ___PPC_RS(4) | (val & 0xffff))); |
|---|
| 161 | 161 | } |
|---|
| 162 | 162 | |
|---|
| 163 | 163 | /* |
|---|
| 164 | 164 | * Generate instructions to load provided immediate 64-bit value |
|---|
| 165 | | - * to register 'r3' and patch these instructions at 'addr'. |
|---|
| 165 | + * to register 'reg' and patch these instructions at 'addr'. |
|---|
| 166 | 166 | */ |
|---|
| 167 | | -void patch_imm64_load_insns(unsigned long val, kprobe_opcode_t *addr) |
|---|
| 167 | +void patch_imm64_load_insns(unsigned long val, int reg, kprobe_opcode_t *addr) |
|---|
| 168 | 168 | { |
|---|
| 169 | | - /* lis r3,(op)@highest */ |
|---|
| 170 | | - patch_instruction(addr, PPC_INST_ADDIS | ___PPC_RT(3) | |
|---|
| 171 | | - ((val >> 48) & 0xffff)); |
|---|
| 169 | + /* lis reg,(op)@highest */ |
|---|
| 170 | + patch_instruction((struct ppc_inst *)addr, |
|---|
| 171 | + ppc_inst(PPC_INST_ADDIS | ___PPC_RT(reg) | |
|---|
| 172 | + ((val >> 48) & 0xffff))); |
|---|
| 172 | 173 | addr++; |
|---|
| 173 | 174 | |
|---|
| 174 | | - /* ori r3,r3,(op)@higher */ |
|---|
| 175 | | - patch_instruction(addr, PPC_INST_ORI | ___PPC_RA(3) | |
|---|
| 176 | | - ___PPC_RS(3) | ((val >> 32) & 0xffff)); |
|---|
| 175 | + /* ori reg,reg,(op)@higher */ |
|---|
| 176 | + patch_instruction((struct ppc_inst *)addr, |
|---|
| 177 | + ppc_inst(PPC_INST_ORI | ___PPC_RA(reg) | |
|---|
| 178 | + ___PPC_RS(reg) | ((val >> 32) & 0xffff))); |
|---|
| 177 | 179 | addr++; |
|---|
| 178 | 180 | |
|---|
| 179 | | - /* rldicr r3,r3,32,31 */ |
|---|
| 180 | | - patch_instruction(addr, PPC_INST_RLDICR | ___PPC_RA(3) | |
|---|
| 181 | | - ___PPC_RS(3) | __PPC_SH64(32) | __PPC_ME64(31)); |
|---|
| 181 | + /* rldicr reg,reg,32,31 */ |
|---|
| 182 | + patch_instruction((struct ppc_inst *)addr, |
|---|
| 183 | + ppc_inst(PPC_INST_RLDICR | ___PPC_RA(reg) | |
|---|
| 184 | + ___PPC_RS(reg) | __PPC_SH64(32) | __PPC_ME64(31))); |
|---|
| 182 | 185 | addr++; |
|---|
| 183 | 186 | |
|---|
| 184 | | - /* oris r3,r3,(op)@h */ |
|---|
| 185 | | - patch_instruction(addr, PPC_INST_ORIS | ___PPC_RA(3) | |
|---|
| 186 | | - ___PPC_RS(3) | ((val >> 16) & 0xffff)); |
|---|
| 187 | + /* oris reg,reg,(op)@h */ |
|---|
| 188 | + patch_instruction((struct ppc_inst *)addr, |
|---|
| 189 | + ppc_inst(PPC_INST_ORIS | ___PPC_RA(reg) | |
|---|
| 190 | + ___PPC_RS(reg) | ((val >> 16) & 0xffff))); |
|---|
| 187 | 191 | addr++; |
|---|
| 188 | 192 | |
|---|
| 189 | | - /* ori r3,r3,(op)@l */ |
|---|
| 190 | | - patch_instruction(addr, PPC_INST_ORI | ___PPC_RA(3) | |
|---|
| 191 | | - ___PPC_RS(3) | (val & 0xffff)); |
|---|
| 193 | + /* ori reg,reg,(op)@l */ |
|---|
| 194 | + patch_instruction((struct ppc_inst *)addr, |
|---|
| 195 | + ppc_inst(PPC_INST_ORI | ___PPC_RA(reg) | |
|---|
| 196 | + ___PPC_RS(reg) | (val & 0xffff))); |
|---|
| 192 | 197 | } |
|---|
| 193 | 198 | |
|---|
| 194 | 199 | int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p) |
|---|
| 195 | 200 | { |
|---|
| 196 | | - kprobe_opcode_t *buff, branch_op_callback, branch_emulate_step; |
|---|
| 197 | | - kprobe_opcode_t *op_callback_addr, *emulate_step_addr; |
|---|
| 201 | + struct ppc_inst branch_op_callback, branch_emulate_step, temp; |
|---|
| 202 | + kprobe_opcode_t *op_callback_addr, *emulate_step_addr, *buff; |
|---|
| 198 | 203 | long b_offset; |
|---|
| 199 | 204 | unsigned long nip, size; |
|---|
| 200 | 205 | int rc, i; |
|---|
| .. | .. |
|---|
| 234 | 239 | size = (TMPL_END_IDX * sizeof(kprobe_opcode_t)) / sizeof(int); |
|---|
| 235 | 240 | pr_devel("Copying template to %p, size %lu\n", buff, size); |
|---|
| 236 | 241 | for (i = 0; i < size; i++) { |
|---|
| 237 | | - rc = patch_instruction(buff + i, *(optprobe_template_entry + i)); |
|---|
| 242 | + rc = patch_instruction((struct ppc_inst *)(buff + i), |
|---|
| 243 | + ppc_inst(*(optprobe_template_entry + i))); |
|---|
| 238 | 244 | if (rc < 0) |
|---|
| 239 | 245 | goto error; |
|---|
| 240 | 246 | } |
|---|
| .. | .. |
|---|
| 243 | 249 | * Fixup the template with instructions to: |
|---|
| 244 | 250 | * 1. load the address of the actual probepoint |
|---|
| 245 | 251 | */ |
|---|
| 246 | | - patch_imm64_load_insns((unsigned long)op, buff + TMPL_OP_IDX); |
|---|
| 252 | + patch_imm64_load_insns((unsigned long)op, 3, buff + TMPL_OP_IDX); |
|---|
| 247 | 253 | |
|---|
| 248 | 254 | /* |
|---|
| 249 | 255 | * 2. branch to optimized_callback() and emulate_step() |
|---|
| .. | .. |
|---|
| 255 | 261 | goto error; |
|---|
| 256 | 262 | } |
|---|
| 257 | 263 | |
|---|
| 258 | | - branch_op_callback = create_branch((unsigned int *)buff + TMPL_CALL_HDLR_IDX, |
|---|
| 259 | | - (unsigned long)op_callback_addr, |
|---|
| 260 | | - BRANCH_SET_LINK); |
|---|
| 264 | + rc = create_branch(&branch_op_callback, |
|---|
| 265 | + (struct ppc_inst *)(buff + TMPL_CALL_HDLR_IDX), |
|---|
| 266 | + (unsigned long)op_callback_addr, |
|---|
| 267 | + BRANCH_SET_LINK); |
|---|
| 261 | 268 | |
|---|
| 262 | | - branch_emulate_step = create_branch((unsigned int *)buff + TMPL_EMULATE_IDX, |
|---|
| 263 | | - (unsigned long)emulate_step_addr, |
|---|
| 264 | | - BRANCH_SET_LINK); |
|---|
| 269 | + rc |= create_branch(&branch_emulate_step, |
|---|
| 270 | + (struct ppc_inst *)(buff + TMPL_EMULATE_IDX), |
|---|
| 271 | + (unsigned long)emulate_step_addr, |
|---|
| 272 | + BRANCH_SET_LINK); |
|---|
| 265 | 273 | |
|---|
| 266 | | - if (!branch_op_callback || !branch_emulate_step) |
|---|
| 274 | + if (rc) |
|---|
| 267 | 275 | goto error; |
|---|
| 268 | 276 | |
|---|
| 269 | | - patch_instruction(buff + TMPL_CALL_HDLR_IDX, branch_op_callback); |
|---|
| 270 | | - patch_instruction(buff + TMPL_EMULATE_IDX, branch_emulate_step); |
|---|
| 277 | + patch_instruction((struct ppc_inst *)(buff + TMPL_CALL_HDLR_IDX), |
|---|
| 278 | + branch_op_callback); |
|---|
| 279 | + patch_instruction((struct ppc_inst *)(buff + TMPL_EMULATE_IDX), |
|---|
| 280 | + branch_emulate_step); |
|---|
| 271 | 281 | |
|---|
| 272 | 282 | /* |
|---|
| 273 | 283 | * 3. load instruction to be emulated into relevant register, and |
|---|
| 274 | 284 | */ |
|---|
| 275 | | - patch_imm32_load_insns(*p->ainsn.insn, buff + TMPL_INSN_IDX); |
|---|
| 285 | + temp = ppc_inst_read((struct ppc_inst *)p->ainsn.insn); |
|---|
| 286 | + patch_imm64_load_insns(ppc_inst_as_u64(temp), 4, buff + TMPL_INSN_IDX); |
|---|
| 276 | 287 | |
|---|
| 277 | 288 | /* |
|---|
| 278 | 289 | * 4. branch back from trampoline |
|---|
| 279 | 290 | */ |
|---|
| 280 | | - patch_branch(buff + TMPL_RET_IDX, (unsigned long)nip, 0); |
|---|
| 291 | + patch_branch((struct ppc_inst *)(buff + TMPL_RET_IDX), (unsigned long)nip, 0); |
|---|
| 281 | 292 | |
|---|
| 282 | 293 | flush_icache_range((unsigned long)buff, |
|---|
| 283 | 294 | (unsigned long)(&buff[TMPL_END_IDX])); |
|---|
| .. | .. |
|---|
| 309 | 320 | |
|---|
| 310 | 321 | void arch_optimize_kprobes(struct list_head *oplist) |
|---|
| 311 | 322 | { |
|---|
| 323 | + struct ppc_inst instr; |
|---|
| 312 | 324 | struct optimized_kprobe *op; |
|---|
| 313 | 325 | struct optimized_kprobe *tmp; |
|---|
| 314 | 326 | |
|---|
| .. | .. |
|---|
| 319 | 331 | */ |
|---|
| 320 | 332 | memcpy(op->optinsn.copied_insn, op->kp.addr, |
|---|
| 321 | 333 | RELATIVEJUMP_SIZE); |
|---|
| 322 | | - patch_instruction(op->kp.addr, |
|---|
| 323 | | - create_branch((unsigned int *)op->kp.addr, |
|---|
| 324 | | - (unsigned long)op->optinsn.insn, 0)); |
|---|
| 334 | + create_branch(&instr, |
|---|
| 335 | + (struct ppc_inst *)op->kp.addr, |
|---|
| 336 | + (unsigned long)op->optinsn.insn, 0); |
|---|
| 337 | + patch_instruction((struct ppc_inst *)op->kp.addr, instr); |
|---|
| 325 | 338 | list_del_init(&op->list); |
|---|
| 326 | 339 | } |
|---|
| 327 | 340 | } |
|---|