Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * arch/arm64/kernel/probes/simulate-insn.c
0004  *
0005  * Copyright (C) 2013 Linaro Limited.
0006  */
0007 
0008 #include <linux/bitops.h>
0009 #include <linux/kernel.h>
0010 #include <linux/kprobes.h>
0011 
0012 #include <asm/ptrace.h>
0013 #include <asm/traps.h>
0014 
0015 #include "simulate-insn.h"
0016 
0017 #define bbl_displacement(insn)      \
0018     sign_extend32(((insn) & 0x3ffffff) << 2, 27)
0019 
0020 #define bcond_displacement(insn)    \
0021     sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
0022 
0023 #define cbz_displacement(insn)  \
0024     sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
0025 
0026 #define tbz_displacement(insn)  \
0027     sign_extend32(((insn >> 5) & 0x3fff) << 2, 15)
0028 
0029 #define ldr_displacement(insn)  \
0030     sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
0031 
0032 static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val)
0033 {
0034     pt_regs_write_reg(regs, reg, val);
0035 }
0036 
0037 static inline void set_w_reg(struct pt_regs *regs, int reg, u64 val)
0038 {
0039     pt_regs_write_reg(regs, reg, lower_32_bits(val));
0040 }
0041 
0042 static inline u64 get_x_reg(struct pt_regs *regs, int reg)
0043 {
0044     return pt_regs_read_reg(regs, reg);
0045 }
0046 
0047 static inline u32 get_w_reg(struct pt_regs *regs, int reg)
0048 {
0049     return lower_32_bits(pt_regs_read_reg(regs, reg));
0050 }
0051 
0052 static bool __kprobes check_cbz(u32 opcode, struct pt_regs *regs)
0053 {
0054     int xn = opcode & 0x1f;
0055 
0056     return (opcode & (1 << 31)) ?
0057         (get_x_reg(regs, xn) == 0) : (get_w_reg(regs, xn) == 0);
0058 }
0059 
0060 static bool __kprobes check_cbnz(u32 opcode, struct pt_regs *regs)
0061 {
0062     int xn = opcode & 0x1f;
0063 
0064     return (opcode & (1 << 31)) ?
0065         (get_x_reg(regs, xn) != 0) : (get_w_reg(regs, xn) != 0);
0066 }
0067 
0068 static bool __kprobes check_tbz(u32 opcode, struct pt_regs *regs)
0069 {
0070     int xn = opcode & 0x1f;
0071     int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
0072 
0073     return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) == 0;
0074 }
0075 
0076 static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)
0077 {
0078     int xn = opcode & 0x1f;
0079     int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
0080 
0081     return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) != 0;
0082 }
0083 
0084 /*
0085  * instruction simulation functions
0086  */
0087 void __kprobes
0088 simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs)
0089 {
0090     long imm, xn, val;
0091 
0092     xn = opcode & 0x1f;
0093     imm = ((opcode >> 3) & 0x1ffffc) | ((opcode >> 29) & 0x3);
0094     imm = sign_extend64(imm, 20);
0095     if (opcode & 0x80000000)
0096         val = (imm<<12) + (addr & 0xfffffffffffff000);
0097     else
0098         val = imm + addr;
0099 
0100     set_x_reg(regs, xn, val);
0101 
0102     instruction_pointer_set(regs, instruction_pointer(regs) + 4);
0103 }
0104 
0105 void __kprobes
0106 simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
0107 {
0108     int disp = bbl_displacement(opcode);
0109 
0110     /* Link register is x30 */
0111     if (opcode & (1 << 31))
0112         set_x_reg(regs, 30, addr + 4);
0113 
0114     instruction_pointer_set(regs, addr + disp);
0115 }
0116 
0117 void __kprobes
0118 simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs)
0119 {
0120     int disp = 4;
0121 
0122     if (aarch32_opcode_cond_checks[opcode & 0xf](regs->pstate & 0xffffffff))
0123         disp = bcond_displacement(opcode);
0124 
0125     instruction_pointer_set(regs, addr + disp);
0126 }
0127 
0128 void __kprobes
0129 simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs)
0130 {
0131     int xn = (opcode >> 5) & 0x1f;
0132 
0133     /* update pc first in case we're doing a "blr lr" */
0134     instruction_pointer_set(regs, get_x_reg(regs, xn));
0135 
0136     /* Link register is x30 */
0137     if (((opcode >> 21) & 0x3) == 1)
0138         set_x_reg(regs, 30, addr + 4);
0139 }
0140 
0141 void __kprobes
0142 simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs)
0143 {
0144     int disp = 4;
0145 
0146     if (opcode & (1 << 24)) {
0147         if (check_cbnz(opcode, regs))
0148             disp = cbz_displacement(opcode);
0149     } else {
0150         if (check_cbz(opcode, regs))
0151             disp = cbz_displacement(opcode);
0152     }
0153     instruction_pointer_set(regs, addr + disp);
0154 }
0155 
0156 void __kprobes
0157 simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
0158 {
0159     int disp = 4;
0160 
0161     if (opcode & (1 << 24)) {
0162         if (check_tbnz(opcode, regs))
0163             disp = tbz_displacement(opcode);
0164     } else {
0165         if (check_tbz(opcode, regs))
0166             disp = tbz_displacement(opcode);
0167     }
0168     instruction_pointer_set(regs, addr + disp);
0169 }
0170 
0171 void __kprobes
0172 simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
0173 {
0174     u64 *load_addr;
0175     int xn = opcode & 0x1f;
0176     int disp;
0177 
0178     disp = ldr_displacement(opcode);
0179     load_addr = (u64 *) (addr + disp);
0180 
0181     if (opcode & (1 << 30)) /* x0-x30 */
0182         set_x_reg(regs, xn, *load_addr);
0183     else            /* w0-w30 */
0184         set_w_reg(regs, xn, *load_addr);
0185 
0186     instruction_pointer_set(regs, instruction_pointer(regs) + 4);
0187 }
0188 
0189 void __kprobes
0190 simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
0191 {
0192     s32 *load_addr;
0193     int xn = opcode & 0x1f;
0194     int disp;
0195 
0196     disp = ldr_displacement(opcode);
0197     load_addr = (s32 *) (addr + disp);
0198 
0199     set_x_reg(regs, xn, *load_addr);
0200 
0201     instruction_pointer_set(regs, instruction_pointer(regs) + 4);
0202 }