blob: ac7b87c228b94f2af114d0a212c4392865e40a64 [file] [log] [blame]
Ricardo Neri32542ee2017-10-27 13:25:36 -07001/*
2 * Utility functions for x86 operand and address decoding
3 *
4 * Copyright (C) Intel Corporation 2017
5 */
6#include <linux/kernel.h>
7#include <linux/string.h>
Ricardo Neried594e42017-10-27 13:25:37 -07008#include <linux/ratelimit.h>
Ricardo Neri32542ee2017-10-27 13:25:36 -07009#include <asm/inat.h>
10#include <asm/insn.h>
11#include <asm/insn-eval.h>
12
Ricardo Neried594e42017-10-27 13:25:37 -070013#undef pr_fmt
14#define pr_fmt(fmt) "insn: " fmt
15
Ricardo Neri32542ee2017-10-27 13:25:36 -070016enum reg_type {
17 REG_TYPE_RM = 0,
18 REG_TYPE_INDEX,
19 REG_TYPE_BASE,
20};
21
Ricardo Neri536b8152017-10-27 13:25:39 -070022/**
23 * is_string_insn() - Determine if instruction is a string instruction
24 * @insn: Instruction containing the opcode to inspect
25 *
26 * Returns:
27 *
28 * true if the instruction, determined by the opcode, is any of the
29 * string instructions as defined in the Intel Software Development manual.
30 * False otherwise.
31 */
32static bool is_string_insn(struct insn *insn)
33{
34 insn_get_opcode(insn);
35
36 /* All string instructions have a 1-byte opcode. */
37 if (insn->opcode.nbytes != 1)
38 return false;
39
40 switch (insn->opcode.bytes[0]) {
41 case 0x6c ... 0x6f: /* INS, OUTS */
42 case 0xa4 ... 0xa7: /* MOVS, CMPS */
43 case 0xaa ... 0xaf: /* STOS, LODS, SCAS */
44 return true;
45 default:
46 return false;
47 }
48}
49
Ricardo Neri32542ee2017-10-27 13:25:36 -070050static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
51 enum reg_type type)
52{
53 int regno = 0;
54
55 static const int regoff[] = {
56 offsetof(struct pt_regs, ax),
57 offsetof(struct pt_regs, cx),
58 offsetof(struct pt_regs, dx),
59 offsetof(struct pt_regs, bx),
60 offsetof(struct pt_regs, sp),
61 offsetof(struct pt_regs, bp),
62 offsetof(struct pt_regs, si),
63 offsetof(struct pt_regs, di),
64#ifdef CONFIG_X86_64
65 offsetof(struct pt_regs, r8),
66 offsetof(struct pt_regs, r9),
67 offsetof(struct pt_regs, r10),
68 offsetof(struct pt_regs, r11),
69 offsetof(struct pt_regs, r12),
70 offsetof(struct pt_regs, r13),
71 offsetof(struct pt_regs, r14),
72 offsetof(struct pt_regs, r15),
73#endif
74 };
75 int nr_registers = ARRAY_SIZE(regoff);
76 /*
77 * Don't possibly decode a 32-bit instructions as
78 * reading a 64-bit-only register.
79 */
80 if (IS_ENABLED(CONFIG_X86_64) && !insn->x86_64)
81 nr_registers -= 8;
82
83 switch (type) {
84 case REG_TYPE_RM:
85 regno = X86_MODRM_RM(insn->modrm.value);
86 if (X86_REX_B(insn->rex_prefix.value))
87 regno += 8;
88 break;
89
90 case REG_TYPE_INDEX:
91 regno = X86_SIB_INDEX(insn->sib.value);
92 if (X86_REX_X(insn->rex_prefix.value))
93 regno += 8;
94
95 /*
96 * If ModRM.mod != 3 and SIB.index = 4 the scale*index
97 * portion of the address computation is null. This is
98 * true only if REX.X is 0. In such a case, the SIB index
99 * is used in the address computation.
100 */
101 if (X86_MODRM_MOD(insn->modrm.value) != 3 && regno == 4)
102 return -EDOM;
103 break;
104
105 case REG_TYPE_BASE:
106 regno = X86_SIB_BASE(insn->sib.value);
107 /*
108 * If ModRM.mod is 0 and SIB.base == 5, the base of the
109 * register-indirect addressing is 0. In this case, a
110 * 32-bit displacement follows the SIB byte.
111 */
112 if (!X86_MODRM_MOD(insn->modrm.value) && regno == 5)
113 return -EDOM;
114
115 if (X86_REX_B(insn->rex_prefix.value))
116 regno += 8;
117 break;
118
119 default:
Ricardo Neried594e42017-10-27 13:25:37 -0700120 pr_err_ratelimited("invalid register type: %d\n", type);
121 return -EINVAL;
Ricardo Neri32542ee2017-10-27 13:25:36 -0700122 }
123
124 if (regno >= nr_registers) {
125 WARN_ONCE(1, "decoded an instruction with an invalid register");
126 return -EINVAL;
127 }
128 return regoff[regno];
129}
130
Ricardo Nerie5e45f12017-10-27 13:25:38 -0700131/**
132 * insn_get_modrm_rm_off() - Obtain register in r/m part of the ModRM byte
133 * @insn: Instruction containing the ModRM byte
134 * @regs: Register values as seen when entering kernel mode
135 *
136 * Returns:
137 *
138 * The register indicated by the r/m part of the ModRM byte. The
139 * register is obtained as an offset from the base of pt_regs. In specific
140 * cases, the returned value can be -EDOM to indicate that the particular value
141 * of ModRM does not refer to a register and shall be ignored.
142 */
143int insn_get_modrm_rm_off(struct insn *insn, struct pt_regs *regs)
144{
145 return get_reg_offset(insn, regs, REG_TYPE_RM);
146}
147
Ricardo Neri32542ee2017-10-27 13:25:36 -0700148/*
149 * return the address being referenced be instruction
150 * for rm=3 returning the content of the rm reg
151 * for rm!=3 calculates the address using SIB and Disp
152 */
153void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
154{
155 int addr_offset, base_offset, indx_offset;
156 unsigned long linear_addr = -1L;
157 long eff_addr, base, indx;
158 insn_byte_t sib;
159
160 insn_get_modrm(insn);
161 insn_get_sib(insn);
162 sib = insn->sib.value;
163
164 if (X86_MODRM_MOD(insn->modrm.value) == 3) {
165 addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
166 if (addr_offset < 0)
167 goto out;
168
169 eff_addr = regs_get_register(regs, addr_offset);
170 } else {
171 if (insn->sib.nbytes) {
172 /*
173 * Negative values in the base and index offset means
174 * an error when decoding the SIB byte. Except -EDOM,
175 * which means that the registers should not be used
176 * in the address computation.
177 */
178 base_offset = get_reg_offset(insn, regs, REG_TYPE_BASE);
179 if (base_offset == -EDOM)
180 base = 0;
181 else if (base_offset < 0)
182 goto out;
183 else
184 base = regs_get_register(regs, base_offset);
185
186 indx_offset = get_reg_offset(insn, regs, REG_TYPE_INDEX);
187
188 if (indx_offset == -EDOM)
189 indx = 0;
190 else if (indx_offset < 0)
191 goto out;
192 else
193 indx = regs_get_register(regs, indx_offset);
194
195 eff_addr = base + indx * (1 << X86_SIB_SCALE(sib));
196 } else {
197 addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
198 if (addr_offset < 0)
199 goto out;
200
201 eff_addr = regs_get_register(regs, addr_offset);
202 }
203
204 eff_addr += insn->displacement.value;
205 }
206
207 linear_addr = (unsigned long)eff_addr;
208
209out:
210 return (void __user *)linear_addr;
211}