arm64: add conditional instruction simulation support
Cease using the arm32 arm_check_condition() function and replace it with a local version for use in deprecated instruction support on arm64. Also make the function table used by this available for future use by kprobes and/or uprobes. This function is derived from code written by Sandeepa Prabhu. Signed-off-by: Sandeepa Prabhu <sandeepa.s.prabhu@gmail.com> Signed-off-by: David A. Long <dave.long@linaro.org> Acked-by: Masami Hiramatsu <mhiramat@kernel.org> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
parent
d59bee8872
commit
2af3ec08b4
|
@ -406,6 +406,9 @@ u32 aarch64_insn_extract_system_reg(u32 insn);
|
||||||
u32 aarch32_insn_extract_reg_num(u32 insn, int offset);
|
u32 aarch32_insn_extract_reg_num(u32 insn, int offset);
|
||||||
u32 aarch32_insn_mcr_extract_opc2(u32 insn);
|
u32 aarch32_insn_mcr_extract_opc2(u32 insn);
|
||||||
u32 aarch32_insn_mcr_extract_crm(u32 insn);
|
u32 aarch32_insn_mcr_extract_crm(u32 insn);
|
||||||
|
|
||||||
|
typedef bool (pstate_check_t)(unsigned long);
|
||||||
|
extern pstate_check_t * const aarch32_opcode_cond_checks[16];
|
||||||
#endif /* __ASSEMBLY__ */
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
#endif /* __ASM_INSN_H */
|
#endif /* __ASM_INSN_H */
|
||||||
|
|
|
@ -26,8 +26,7 @@ $(obj)/%.stub.o: $(obj)/%.o FORCE
|
||||||
$(call if_changed,objcopy)
|
$(call if_changed,objcopy)
|
||||||
|
|
||||||
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
|
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
|
||||||
sys_compat.o entry32.o \
|
sys_compat.o entry32.o
|
||||||
../../arm/kernel/opcodes.o
|
|
||||||
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
|
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
|
||||||
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
|
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
|
||||||
arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o
|
arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o
|
||||||
|
|
|
@ -366,6 +366,21 @@ static int emulate_swpX(unsigned int address, unsigned int *data,
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define ARM_OPCODE_CONDITION_UNCOND 0xf
|
||||||
|
|
||||||
|
static unsigned int __kprobes aarch32_check_condition(u32 opcode, u32 psr)
|
||||||
|
{
|
||||||
|
u32 cc_bits = opcode >> 28;
|
||||||
|
|
||||||
|
if (cc_bits != ARM_OPCODE_CONDITION_UNCOND) {
|
||||||
|
if ((*aarch32_opcode_cond_checks[cc_bits])(psr))
|
||||||
|
return ARM_OPCODE_CONDTEST_PASS;
|
||||||
|
else
|
||||||
|
return ARM_OPCODE_CONDTEST_FAIL;
|
||||||
|
}
|
||||||
|
return ARM_OPCODE_CONDTEST_UNCOND;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* swp_handler logs the id of calling process, dissects the instruction, sanity
|
* swp_handler logs the id of calling process, dissects the instruction, sanity
|
||||||
* checks the memory location, calls emulate_swpX for the actual operation and
|
* checks the memory location, calls emulate_swpX for the actual operation and
|
||||||
|
@ -380,7 +395,7 @@ static int swp_handler(struct pt_regs *regs, u32 instr)
|
||||||
|
|
||||||
type = instr & TYPE_SWPB;
|
type = instr & TYPE_SWPB;
|
||||||
|
|
||||||
switch (arm_check_condition(instr, regs->pstate)) {
|
switch (aarch32_check_condition(instr, regs->pstate)) {
|
||||||
case ARM_OPCODE_CONDTEST_PASS:
|
case ARM_OPCODE_CONDTEST_PASS:
|
||||||
break;
|
break;
|
||||||
case ARM_OPCODE_CONDTEST_FAIL:
|
case ARM_OPCODE_CONDTEST_FAIL:
|
||||||
|
@ -461,7 +476,7 @@ static int cp15barrier_handler(struct pt_regs *regs, u32 instr)
|
||||||
{
|
{
|
||||||
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
|
perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->pc);
|
||||||
|
|
||||||
switch (arm_check_condition(instr, regs->pstate)) {
|
switch (aarch32_check_condition(instr, regs->pstate)) {
|
||||||
case ARM_OPCODE_CONDTEST_PASS:
|
case ARM_OPCODE_CONDTEST_PASS:
|
||||||
break;
|
break;
|
||||||
case ARM_OPCODE_CONDTEST_FAIL:
|
case ARM_OPCODE_CONDTEST_FAIL:
|
||||||
|
|
|
@ -1234,3 +1234,101 @@ u32 aarch32_insn_mcr_extract_crm(u32 insn)
|
||||||
{
|
{
|
||||||
return insn & CRM_MASK;
|
return insn & CRM_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_eq(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_Z_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_ne(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_Z_BIT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_cs(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_C_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_cc(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_C_BIT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_mi(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_N_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_pl(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_N_BIT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_vs(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_V_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_vc(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return (pstate & PSR_V_BIT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_hi(unsigned long pstate)
|
||||||
|
{
|
||||||
|
pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
|
||||||
|
return (pstate & PSR_C_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_ls(unsigned long pstate)
|
||||||
|
{
|
||||||
|
pstate &= ~(pstate >> 1); /* PSR_C_BIT &= ~PSR_Z_BIT */
|
||||||
|
return (pstate & PSR_C_BIT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_ge(unsigned long pstate)
|
||||||
|
{
|
||||||
|
pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||||
|
return (pstate & PSR_N_BIT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_lt(unsigned long pstate)
|
||||||
|
{
|
||||||
|
pstate ^= (pstate << 3); /* PSR_N_BIT ^= PSR_V_BIT */
|
||||||
|
return (pstate & PSR_N_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_gt(unsigned long pstate)
|
||||||
|
{
|
||||||
|
/*PSR_N_BIT ^= PSR_V_BIT */
|
||||||
|
unsigned long temp = pstate ^ (pstate << 3);
|
||||||
|
|
||||||
|
temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
|
||||||
|
return (temp & PSR_N_BIT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_le(unsigned long pstate)
|
||||||
|
{
|
||||||
|
/*PSR_N_BIT ^= PSR_V_BIT */
|
||||||
|
unsigned long temp = pstate ^ (pstate << 3);
|
||||||
|
|
||||||
|
temp |= (pstate << 1); /*PSR_N_BIT |= PSR_Z_BIT */
|
||||||
|
return (temp & PSR_N_BIT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool __kprobes __check_al(unsigned long pstate)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note that the ARMv8 ARM calls condition code 0b1111 "nv", but states that
|
||||||
|
* it behaves identically to 0b1110 ("al").
|
||||||
|
*/
|
||||||
|
pstate_check_t * const aarch32_opcode_cond_checks[16] = {
|
||||||
|
__check_eq, __check_ne, __check_cs, __check_cc,
|
||||||
|
__check_mi, __check_pl, __check_vs, __check_vc,
|
||||||
|
__check_hi, __check_ls, __check_ge, __check_lt,
|
||||||
|
__check_gt, __check_le, __check_al, __check_al
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue