mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-28 07:04:00 +08:00
32542ee295
Other kernel submodules can benefit from using the utility functions defined in mpx.c to obtain the addresses and values of operands contained in the general purpose registers. An instance of this is the emulation code used for instructions protected by the Intel User-Mode Instruction Prevention feature. Thus, these functions are relocated to a new insn-eval.c file. The reason to not relocate these utilities into insn.c is that the latter solely analyses instructions given by a struct insn without any knowledge of the meaning of the values of instruction operands. This new utility insn- eval.c aims to be used to resolve userspace linear addresses based on the contents of the instruction operands as well as the contents of pt_regs structure. These utilities come with a separate header. This is to avoid taking insn.c out of sync from the instructions decoders under tools/obj and tools/perf. This also avoids adding cumbersome #ifdef's for the #include'd files required to decode instructions in a kernel context. Functions are simply relocated. There are not functional or indentation changes. Signed-off-by: Ricardo Neri <ricardo.neri-calderon@linux.intel.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Borislav Petkov <bp@suse.de> Cc: "Michael S. Tsirkin" <mst@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: ricardo.neri@intel.com Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Paul Gortmaker <paul.gortmaker@windriver.com> Cc: Huang Rui <ray.huang@amd.com> Cc: Qiaowei Ren <qiaowei.ren@intel.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Kees Cook <keescook@chromium.org> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Jiri Slaby <jslaby@suse.cz> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: "Ravi V. Shankar" <ravi.v.shankar@intel.com> Cc: Chris Metcalf <cmetcalf@mellanox.com> Cc: Brian Gerst <brgerst@gmail.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: Colin Ian King <colin.king@canonical.com> Cc: Chen Yucong <slaoub@gmail.com> Cc: Adam Buchbinder <adam.buchbinder@gmail.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Lorenzo Stoakes <lstoakes@gmail.com> Cc: Masami Hiramatsu <mhiramat@kernel.org> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Thomas Garnier <thgarnie@google.com> Link: https://lkml.kernel.org/r/1509135945-13762-10-git-send-email-ricardo.neri-calderon@linux.intel.com
164 lines
3.9 KiB
C
164 lines
3.9 KiB
C
/*
|
|
* Utility functions for x86 operand and address decoding
|
|
*
|
|
* Copyright (C) Intel Corporation 2017
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/string.h>
|
|
#include <asm/inat.h>
|
|
#include <asm/insn.h>
|
|
#include <asm/insn-eval.h>
|
|
|
|
enum reg_type {
|
|
REG_TYPE_RM = 0,
|
|
REG_TYPE_INDEX,
|
|
REG_TYPE_BASE,
|
|
};
|
|
|
|
static int get_reg_offset(struct insn *insn, struct pt_regs *regs,
|
|
enum reg_type type)
|
|
{
|
|
int regno = 0;
|
|
|
|
static const int regoff[] = {
|
|
offsetof(struct pt_regs, ax),
|
|
offsetof(struct pt_regs, cx),
|
|
offsetof(struct pt_regs, dx),
|
|
offsetof(struct pt_regs, bx),
|
|
offsetof(struct pt_regs, sp),
|
|
offsetof(struct pt_regs, bp),
|
|
offsetof(struct pt_regs, si),
|
|
offsetof(struct pt_regs, di),
|
|
#ifdef CONFIG_X86_64
|
|
offsetof(struct pt_regs, r8),
|
|
offsetof(struct pt_regs, r9),
|
|
offsetof(struct pt_regs, r10),
|
|
offsetof(struct pt_regs, r11),
|
|
offsetof(struct pt_regs, r12),
|
|
offsetof(struct pt_regs, r13),
|
|
offsetof(struct pt_regs, r14),
|
|
offsetof(struct pt_regs, r15),
|
|
#endif
|
|
};
|
|
int nr_registers = ARRAY_SIZE(regoff);
|
|
/*
|
|
* Don't possibly decode a 32-bit instructions as
|
|
* reading a 64-bit-only register.
|
|
*/
|
|
if (IS_ENABLED(CONFIG_X86_64) && !insn->x86_64)
|
|
nr_registers -= 8;
|
|
|
|
switch (type) {
|
|
case REG_TYPE_RM:
|
|
regno = X86_MODRM_RM(insn->modrm.value);
|
|
if (X86_REX_B(insn->rex_prefix.value))
|
|
regno += 8;
|
|
break;
|
|
|
|
case REG_TYPE_INDEX:
|
|
regno = X86_SIB_INDEX(insn->sib.value);
|
|
if (X86_REX_X(insn->rex_prefix.value))
|
|
regno += 8;
|
|
|
|
/*
|
|
* If ModRM.mod != 3 and SIB.index = 4 the scale*index
|
|
* portion of the address computation is null. This is
|
|
* true only if REX.X is 0. In such a case, the SIB index
|
|
* is used in the address computation.
|
|
*/
|
|
if (X86_MODRM_MOD(insn->modrm.value) != 3 && regno == 4)
|
|
return -EDOM;
|
|
break;
|
|
|
|
case REG_TYPE_BASE:
|
|
regno = X86_SIB_BASE(insn->sib.value);
|
|
/*
|
|
* If ModRM.mod is 0 and SIB.base == 5, the base of the
|
|
* register-indirect addressing is 0. In this case, a
|
|
* 32-bit displacement follows the SIB byte.
|
|
*/
|
|
if (!X86_MODRM_MOD(insn->modrm.value) && regno == 5)
|
|
return -EDOM;
|
|
|
|
if (X86_REX_B(insn->rex_prefix.value))
|
|
regno += 8;
|
|
break;
|
|
|
|
default:
|
|
pr_err("invalid register type");
|
|
BUG();
|
|
break;
|
|
}
|
|
|
|
if (regno >= nr_registers) {
|
|
WARN_ONCE(1, "decoded an instruction with an invalid register");
|
|
return -EINVAL;
|
|
}
|
|
return regoff[regno];
|
|
}
|
|
|
|
/*
|
|
* return the address being referenced be instruction
|
|
* for rm=3 returning the content of the rm reg
|
|
* for rm!=3 calculates the address using SIB and Disp
|
|
*/
|
|
void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
|
|
{
|
|
int addr_offset, base_offset, indx_offset;
|
|
unsigned long linear_addr = -1L;
|
|
long eff_addr, base, indx;
|
|
insn_byte_t sib;
|
|
|
|
insn_get_modrm(insn);
|
|
insn_get_sib(insn);
|
|
sib = insn->sib.value;
|
|
|
|
if (X86_MODRM_MOD(insn->modrm.value) == 3) {
|
|
addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
|
|
if (addr_offset < 0)
|
|
goto out;
|
|
|
|
eff_addr = regs_get_register(regs, addr_offset);
|
|
} else {
|
|
if (insn->sib.nbytes) {
|
|
/*
|
|
* Negative values in the base and index offset means
|
|
* an error when decoding the SIB byte. Except -EDOM,
|
|
* which means that the registers should not be used
|
|
* in the address computation.
|
|
*/
|
|
base_offset = get_reg_offset(insn, regs, REG_TYPE_BASE);
|
|
if (base_offset == -EDOM)
|
|
base = 0;
|
|
else if (base_offset < 0)
|
|
goto out;
|
|
else
|
|
base = regs_get_register(regs, base_offset);
|
|
|
|
indx_offset = get_reg_offset(insn, regs, REG_TYPE_INDEX);
|
|
|
|
if (indx_offset == -EDOM)
|
|
indx = 0;
|
|
else if (indx_offset < 0)
|
|
goto out;
|
|
else
|
|
indx = regs_get_register(regs, indx_offset);
|
|
|
|
eff_addr = base + indx * (1 << X86_SIB_SCALE(sib));
|
|
} else {
|
|
addr_offset = get_reg_offset(insn, regs, REG_TYPE_RM);
|
|
if (addr_offset < 0)
|
|
goto out;
|
|
|
|
eff_addr = regs_get_register(regs, addr_offset);
|
|
}
|
|
|
|
eff_addr += insn->displacement.value;
|
|
}
|
|
|
|
linear_addr = (unsigned long)eff_addr;
|
|
|
|
out:
|
|
return (void __user *)linear_addr;
|
|
}
|