Linux Audio

Check our new training course

Loading...
v5.14.15
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * arch/arm64/kernel/probes/simulate-insn.c
  4 *
  5 * Copyright (C) 2013 Linaro Limited.
 
 
 
 
 
 
 
 
 
  6 */
  7
  8#include <linux/bitops.h>
  9#include <linux/kernel.h>
 10#include <linux/kprobes.h>
 11
 12#include <asm/ptrace.h>
 13#include <asm/traps.h>
 14
 15#include "simulate-insn.h"
 16
 17#define bbl_displacement(insn)		\
 18	sign_extend32(((insn) & 0x3ffffff) << 2, 27)
 19
 20#define bcond_displacement(insn)	\
 21	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 22
 23#define cbz_displacement(insn)	\
 24	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 25
 26#define tbz_displacement(insn)	\
 27	sign_extend32(((insn >> 5) & 0x3fff) << 2, 15)
 28
 29#define ldr_displacement(insn)	\
 30	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 31
 32static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val)
 33{
 34	pt_regs_write_reg(regs, reg, val);
 
 35}
 36
 37static inline void set_w_reg(struct pt_regs *regs, int reg, u64 val)
 38{
 39	pt_regs_write_reg(regs, reg, lower_32_bits(val));
 
 40}
 41
 42static inline u64 get_x_reg(struct pt_regs *regs, int reg)
 43{
 44	return pt_regs_read_reg(regs, reg);
 
 
 
 45}
 46
 47static inline u32 get_w_reg(struct pt_regs *regs, int reg)
 48{
 49	return lower_32_bits(pt_regs_read_reg(regs, reg));
 
 
 
 50}
 51
 52static bool __kprobes check_cbz(u32 opcode, struct pt_regs *regs)
 53{
 54	int xn = opcode & 0x1f;
 55
 56	return (opcode & (1 << 31)) ?
 57	    (get_x_reg(regs, xn) == 0) : (get_w_reg(regs, xn) == 0);
 58}
 59
 60static bool __kprobes check_cbnz(u32 opcode, struct pt_regs *regs)
 61{
 62	int xn = opcode & 0x1f;
 63
 64	return (opcode & (1 << 31)) ?
 65	    (get_x_reg(regs, xn) != 0) : (get_w_reg(regs, xn) != 0);
 66}
 67
 68static bool __kprobes check_tbz(u32 opcode, struct pt_regs *regs)
 69{
 70	int xn = opcode & 0x1f;
 71	int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
 72
 73	return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) == 0;
 74}
 75
 76static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)
 77{
 78	int xn = opcode & 0x1f;
 79	int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
 80
 81	return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) != 0;
 82}
 83
 84/*
 85 * instruction simulation functions
 86 */
 87void __kprobes
 88simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs)
 89{
 90	long imm, xn, val;
 91
 92	xn = opcode & 0x1f;
 93	imm = ((opcode >> 3) & 0x1ffffc) | ((opcode >> 29) & 0x3);
 94	imm = sign_extend64(imm, 20);
 95	if (opcode & 0x80000000)
 96		val = (imm<<12) + (addr & 0xfffffffffffff000);
 97	else
 98		val = imm + addr;
 99
100	set_x_reg(regs, xn, val);
101
102	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
103}
104
105void __kprobes
106simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
107{
108	int disp = bbl_displacement(opcode);
109
110	/* Link register is x30 */
111	if (opcode & (1 << 31))
112		set_x_reg(regs, 30, addr + 4);
113
114	instruction_pointer_set(regs, addr + disp);
115}
116
117void __kprobes
118simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs)
119{
120	int disp = 4;
121
122	if (aarch32_opcode_cond_checks[opcode & 0xf](regs->pstate & 0xffffffff))
123		disp = bcond_displacement(opcode);
124
125	instruction_pointer_set(regs, addr + disp);
126}
127
128void __kprobes
129simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs)
130{
131	int xn = (opcode >> 5) & 0x1f;
132
133	/* update pc first in case we're doing a "blr lr" */
134	instruction_pointer_set(regs, get_x_reg(regs, xn));
135
136	/* Link register is x30 */
137	if (((opcode >> 21) & 0x3) == 1)
138		set_x_reg(regs, 30, addr + 4);
139}
140
141void __kprobes
142simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs)
143{
144	int disp = 4;
145
146	if (opcode & (1 << 24)) {
147		if (check_cbnz(opcode, regs))
148			disp = cbz_displacement(opcode);
149	} else {
150		if (check_cbz(opcode, regs))
151			disp = cbz_displacement(opcode);
152	}
153	instruction_pointer_set(regs, addr + disp);
154}
155
156void __kprobes
157simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
158{
159	int disp = 4;
160
161	if (opcode & (1 << 24)) {
162		if (check_tbnz(opcode, regs))
163			disp = tbz_displacement(opcode);
164	} else {
165		if (check_tbz(opcode, regs))
166			disp = tbz_displacement(opcode);
167	}
168	instruction_pointer_set(regs, addr + disp);
169}
170
171void __kprobes
172simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
173{
174	u64 *load_addr;
175	int xn = opcode & 0x1f;
176	int disp;
177
178	disp = ldr_displacement(opcode);
179	load_addr = (u64 *) (addr + disp);
180
181	if (opcode & (1 << 30))	/* x0-x30 */
182		set_x_reg(regs, xn, *load_addr);
183	else			/* w0-w30 */
184		set_w_reg(regs, xn, *load_addr);
185
186	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
187}
188
189void __kprobes
190simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
191{
192	s32 *load_addr;
193	int xn = opcode & 0x1f;
194	int disp;
195
196	disp = ldr_displacement(opcode);
197	load_addr = (s32 *) (addr + disp);
198
199	set_x_reg(regs, xn, *load_addr);
200
201	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
202}
v4.10.11
 
  1/*
  2 * arch/arm64/kernel/probes/simulate-insn.c
  3 *
  4 * Copyright (C) 2013 Linaro Limited.
  5 *
  6 * This program is free software; you can redistribute it and/or modify
  7 * it under the terms of the GNU General Public License version 2 as
  8 * published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope that it will be useful,
 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13 * General Public License for more details.
 14 */
 15
 16#include <linux/bitops.h>
 17#include <linux/kernel.h>
 18#include <linux/kprobes.h>
 19
 
 
 
 20#include "simulate-insn.h"
 21
 22#define bbl_displacement(insn)		\
 23	sign_extend32(((insn) & 0x3ffffff) << 2, 27)
 24
 25#define bcond_displacement(insn)	\
 26	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 27
 28#define cbz_displacement(insn)	\
 29	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 30
 31#define tbz_displacement(insn)	\
 32	sign_extend32(((insn >> 5) & 0x3fff) << 2, 15)
 33
 34#define ldr_displacement(insn)	\
 35	sign_extend32(((insn >> 5) & 0x7ffff) << 2, 20)
 36
 37static inline void set_x_reg(struct pt_regs *regs, int reg, u64 val)
 38{
 39	if (reg < 31)
 40		regs->regs[reg] = val;
 41}
 42
 43static inline void set_w_reg(struct pt_regs *regs, int reg, u64 val)
 44{
 45	if (reg < 31)
 46		regs->regs[reg] = lower_32_bits(val);
 47}
 48
 49static inline u64 get_x_reg(struct pt_regs *regs, int reg)
 50{
 51	if (reg < 31)
 52		return regs->regs[reg];
 53	else
 54		return 0;
 55}
 56
 57static inline u32 get_w_reg(struct pt_regs *regs, int reg)
 58{
 59	if (reg < 31)
 60		return lower_32_bits(regs->regs[reg]);
 61	else
 62		return 0;
 63}
 64
 65static bool __kprobes check_cbz(u32 opcode, struct pt_regs *regs)
 66{
 67	int xn = opcode & 0x1f;
 68
 69	return (opcode & (1 << 31)) ?
 70	    (get_x_reg(regs, xn) == 0) : (get_w_reg(regs, xn) == 0);
 71}
 72
 73static bool __kprobes check_cbnz(u32 opcode, struct pt_regs *regs)
 74{
 75	int xn = opcode & 0x1f;
 76
 77	return (opcode & (1 << 31)) ?
 78	    (get_x_reg(regs, xn) != 0) : (get_w_reg(regs, xn) != 0);
 79}
 80
 81static bool __kprobes check_tbz(u32 opcode, struct pt_regs *regs)
 82{
 83	int xn = opcode & 0x1f;
 84	int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
 85
 86	return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) == 0;
 87}
 88
 89static bool __kprobes check_tbnz(u32 opcode, struct pt_regs *regs)
 90{
 91	int xn = opcode & 0x1f;
 92	int bit_pos = ((opcode & (1 << 31)) >> 26) | ((opcode >> 19) & 0x1f);
 93
 94	return ((get_x_reg(regs, xn) >> bit_pos) & 0x1) != 0;
 95}
 96
 97/*
 98 * instruction simulation functions
 99 */
100void __kprobes
101simulate_adr_adrp(u32 opcode, long addr, struct pt_regs *regs)
102{
103	long imm, xn, val;
104
105	xn = opcode & 0x1f;
106	imm = ((opcode >> 3) & 0x1ffffc) | ((opcode >> 29) & 0x3);
107	imm = sign_extend64(imm, 20);
108	if (opcode & 0x80000000)
109		val = (imm<<12) + (addr & 0xfffffffffffff000);
110	else
111		val = imm + addr;
112
113	set_x_reg(regs, xn, val);
114
115	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
116}
117
118void __kprobes
119simulate_b_bl(u32 opcode, long addr, struct pt_regs *regs)
120{
121	int disp = bbl_displacement(opcode);
122
123	/* Link register is x30 */
124	if (opcode & (1 << 31))
125		set_x_reg(regs, 30, addr + 4);
126
127	instruction_pointer_set(regs, addr + disp);
128}
129
130void __kprobes
131simulate_b_cond(u32 opcode, long addr, struct pt_regs *regs)
132{
133	int disp = 4;
134
135	if (aarch32_opcode_cond_checks[opcode & 0xf](regs->pstate & 0xffffffff))
136		disp = bcond_displacement(opcode);
137
138	instruction_pointer_set(regs, addr + disp);
139}
140
141void __kprobes
142simulate_br_blr_ret(u32 opcode, long addr, struct pt_regs *regs)
143{
144	int xn = (opcode >> 5) & 0x1f;
145
146	/* update pc first in case we're doing a "blr lr" */
147	instruction_pointer_set(regs, get_x_reg(regs, xn));
148
149	/* Link register is x30 */
150	if (((opcode >> 21) & 0x3) == 1)
151		set_x_reg(regs, 30, addr + 4);
152}
153
154void __kprobes
155simulate_cbz_cbnz(u32 opcode, long addr, struct pt_regs *regs)
156{
157	int disp = 4;
158
159	if (opcode & (1 << 24)) {
160		if (check_cbnz(opcode, regs))
161			disp = cbz_displacement(opcode);
162	} else {
163		if (check_cbz(opcode, regs))
164			disp = cbz_displacement(opcode);
165	}
166	instruction_pointer_set(regs, addr + disp);
167}
168
169void __kprobes
170simulate_tbz_tbnz(u32 opcode, long addr, struct pt_regs *regs)
171{
172	int disp = 4;
173
174	if (opcode & (1 << 24)) {
175		if (check_tbnz(opcode, regs))
176			disp = tbz_displacement(opcode);
177	} else {
178		if (check_tbz(opcode, regs))
179			disp = tbz_displacement(opcode);
180	}
181	instruction_pointer_set(regs, addr + disp);
182}
183
184void __kprobes
185simulate_ldr_literal(u32 opcode, long addr, struct pt_regs *regs)
186{
187	u64 *load_addr;
188	int xn = opcode & 0x1f;
189	int disp;
190
191	disp = ldr_displacement(opcode);
192	load_addr = (u64 *) (addr + disp);
193
194	if (opcode & (1 << 30))	/* x0-x30 */
195		set_x_reg(regs, xn, *load_addr);
196	else			/* w0-w30 */
197		set_w_reg(regs, xn, *load_addr);
198
199	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
200}
201
202void __kprobes
203simulate_ldrsw_literal(u32 opcode, long addr, struct pt_regs *regs)
204{
205	s32 *load_addr;
206	int xn = opcode & 0x1f;
207	int disp;
208
209	disp = ldr_displacement(opcode);
210	load_addr = (s32 *) (addr + disp);
211
212	set_x_reg(regs, xn, *load_addr);
213
214	instruction_pointer_set(regs, instruction_pointer(regs) + 4);
215}