mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
230
arch/arm/kernel/uprobes.c
Normal file
230
arch/arm/kernel/uprobes.c
Normal file
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uprobes.h>
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#include <asm/opcodes.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
#include "probes.h"
|
||||
#include "probes-arm.h"
|
||||
#include "uprobes.h"
|
||||
|
||||
#define UPROBE_TRAP_NR UINT_MAX
|
||||
|
||||
bool is_swbp_insn(uprobe_opcode_t *insn)
|
||||
{
|
||||
return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
|
||||
(UPROBE_SWBP_ARM_INSN & 0x0fffffff);
|
||||
}
|
||||
|
||||
int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
||||
unsigned long vaddr)
|
||||
{
|
||||
return uprobe_write_opcode(mm, vaddr,
|
||||
__opcode_to_mem_arm(auprobe->bpinsn));
|
||||
}
|
||||
|
||||
bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
|
||||
regs->ARM_pc += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
probes_opcode_t opcode;
|
||||
|
||||
if (!auprobe->simulate)
|
||||
return false;
|
||||
|
||||
opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
|
||||
|
||||
auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long
|
||||
arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long orig_ret_vaddr;
|
||||
|
||||
orig_ret_vaddr = regs->ARM_lr;
|
||||
/* Replace the return addr with trampoline addr */
|
||||
regs->ARM_lr = trampoline_vaddr;
|
||||
return orig_ret_vaddr;
|
||||
}
|
||||
|
||||
int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
|
||||
unsigned long addr)
|
||||
{
|
||||
unsigned int insn;
|
||||
unsigned int bpinsn;
|
||||
enum probes_insn ret;
|
||||
|
||||
/* Thumb not yet support */
|
||||
if (addr & 0x3)
|
||||
return -EINVAL;
|
||||
|
||||
insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
|
||||
auprobe->ixol[0] = __opcode_to_mem_arm(insn);
|
||||
auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
|
||||
|
||||
ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
|
||||
uprobes_probes_actions);
|
||||
switch (ret) {
|
||||
case INSN_REJECTED:
|
||||
return -EINVAL;
|
||||
|
||||
case INSN_GOOD_NO_SLOT:
|
||||
auprobe->simulate = true;
|
||||
break;
|
||||
|
||||
case INSN_GOOD:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
|
||||
if (insn >= 0xe0000000)
|
||||
bpinsn |= 0xe0000000; /* Unconditional instruction */
|
||||
else
|
||||
bpinsn |= insn & 0xf0000000; /* Copy condition from insn */
|
||||
|
||||
auprobe->bpinsn = bpinsn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
|
||||
void *src, unsigned long len)
|
||||
{
|
||||
void *xol_page_kaddr = kmap_atomic(page);
|
||||
void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK);
|
||||
|
||||
preempt_disable();
|
||||
|
||||
/* Initialize the slot */
|
||||
memcpy(dst, src, len);
|
||||
|
||||
/* flush caches (dcache/icache) */
|
||||
flush_uprobe_xol_access(page, vaddr, dst, len);
|
||||
|
||||
preempt_enable();
|
||||
|
||||
kunmap_atomic(xol_page_kaddr);
|
||||
}
|
||||
|
||||
|
||||
int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
struct uprobe_task *utask = current->utask;
|
||||
|
||||
if (auprobe->prehandler)
|
||||
auprobe->prehandler(auprobe, &utask->autask, regs);
|
||||
|
||||
utask->autask.saved_trap_no = current->thread.trap_no;
|
||||
current->thread.trap_no = UPROBE_TRAP_NR;
|
||||
regs->ARM_pc = utask->xol_vaddr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
struct uprobe_task *utask = current->utask;
|
||||
|
||||
WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
|
||||
|
||||
current->thread.trap_no = utask->autask.saved_trap_no;
|
||||
regs->ARM_pc = utask->vaddr + 4;
|
||||
|
||||
if (auprobe->posthandler)
|
||||
auprobe->posthandler(auprobe, &utask->autask, regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool arch_uprobe_xol_was_trapped(struct task_struct *t)
|
||||
{
|
||||
if (t->thread.trap_no != UPROBE_TRAP_NR)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
|
||||
{
|
||||
struct uprobe_task *utask = current->utask;
|
||||
|
||||
current->thread.trap_no = utask->autask.saved_trap_no;
|
||||
instruction_pointer_set(regs, utask->vaddr);
|
||||
}
|
||||
|
||||
int arch_uprobe_exception_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
local_irq_save(flags);
|
||||
instr &= 0x0fffffff;
|
||||
if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff))
|
||||
uprobe_pre_sstep_notifier(regs);
|
||||
else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff))
|
||||
uprobe_post_sstep_notifier(regs);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
|
||||
{
|
||||
return instruction_pointer(regs);
|
||||
}
|
||||
|
||||
static struct undef_hook uprobes_arm_break_hook = {
|
||||
.instr_mask = 0x0fffffff,
|
||||
.instr_val = (UPROBE_SWBP_ARM_INSN & 0x0fffffff),
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = USR_MODE,
|
||||
.fn = uprobe_trap_handler,
|
||||
};
|
||||
|
||||
static struct undef_hook uprobes_arm_ss_hook = {
|
||||
.instr_mask = 0x0fffffff,
|
||||
.instr_val = (UPROBE_SS_ARM_INSN & 0x0fffffff),
|
||||
.cpsr_mask = MODE_MASK,
|
||||
.cpsr_val = USR_MODE,
|
||||
.fn = uprobe_trap_handler,
|
||||
};
|
||||
|
||||
static int arch_uprobes_init(void)
|
||||
{
|
||||
register_undef_hook(&uprobes_arm_break_hook);
|
||||
register_undef_hook(&uprobes_arm_ss_hook);
|
||||
|
||||
return 0;
|
||||
}
|
||||
device_initcall(arch_uprobes_init);
|
Loading…
Add table
Add a link
Reference in a new issue