\linux-4.4.202\arch\arm\kernel\traps.c
do_undefinstr在__und_fault中被调用
__und_fault:
@纠正PC指针,使其指向
@导致故障的指令。如果发生故障的指令是ARM
@电脑将指向下一条指令,并且PC指针必须
@减去4。如果它是Thumb,PC指针将
@指向Thumb指令的后半部分。并且
@必须减去2。
ldr r2, [r0, #S_PC]
sub r2, r2, r1
str r2, [r0, #S_PC]
b do_undefinstr
ENDPROC(__und_fault)
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
unsigned int instr;
siginfo_t info;
void __user *pc;
pc = (void __user *)instruction_pointer(regs);
if (processor_mode(regs) == SVC_MODE) {
#ifdef CONFIG_THUMB2_KERNEL
if (thumb_mode(regs)) {
instr = __mem_to_opcode_thumb16(((u16 *)pc)[0]);
if (is_wide_instruction(instr)) {
u16 inst2;
inst2 = __mem_to_opcode_thumb16(((u16 )pc)[1]);
instr = __opcode_thumb32_compose(instr, inst2);
}
} else
#endif
instr = __mem_to_opcode_arm((u32 *) pc);
} else if (thumb_mode(regs)) {
if (get_user(instr, (u16 __user *)pc))
goto die_sig;
instr = __mem_to_opcode_thumb16(instr);
if (is_wide_instruction(instr)) {
unsigned int instr2;
if (get_user(instr2, (u16 __user *)pc+1))
goto die_sig;
instr2 = __mem_to_opcode_thumb16(instr2);
instr = __opcode_thumb32_compose(instr, instr2);
}
} else {
if (get_user(instr, (u32 __user *)pc))
goto die_sig;
instr = __mem_to_opcode_arm(instr);
}
if (call_undef_hook(regs, instr) == 0)
return;
die_sig:
#ifdef CONFIG_DEBUG_USER
if (user_debug & UDBG_UNDEFINED) {
pr_info(“%s (%d): undefined instruction: pc=%p\n”,
current->comm, task_pid_nr(current), pc);
__show_regs(regs);
dump_instr(KERN_INFO, regs);
}
#endif
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = pc;
arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6);
}
NOKPROBE_SYMBOL(do_undefinstr)
get_user:
get_user()函数
//x:驱动程序中的变量;
//ptr:用户空间的地址;
#define get_user(x, ptr)
({
const void *__p = (ptr);
might_fault();
access_ok(VERIFY_READ, __p, sizeof(ptr)) ?
__get_user((x), (typeof((ptr)) )__p) :
((x) = (typeof((ptr)))0,-EFAULT);
})
理解方法和put_user()函数一样,不同的是数据的流向,get_user()函数是从用户空间ptr处读取sizeof(*ptr)个字节数据方法放到驱动程序的x变量中;
__mem_to_opcode_arm:将指定地址转换为操作码,可能会32位反转
call_undef_hook:
// 查找列表,对已经注册的函数,进行回调
static int call_undef_hook(struct pt_regs *regs, unsigned int instr)
{
struct undef_hook *hook;
unsigned long flags;
int (*fn)(struct pt_regs *regs, unsigned int instr) = NULL;
// 遍历list(hook),查看哪个注册的指令值与当前的一致
list_for_each_entry(hook, &undef_hook, node)
// instr 与注册的要一致的
// kgdb , ptrace , kprobe 的都不一致
if ((instr & hook->instr_mask) == hook->instr_val &&
(regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val)
//回调函数
fn = hook->fn;
return fn ? fn(regs, instr) : 1;
}
list_for_each_entry应用:
它实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回head(prefetch() 可以不考虑,用于预取以提高遍历速度 )。
在程序中的使用如下:
list_for_each_entry(pos , head,member)
{
………………
addr = pos; //对返回值pos的操作,这样更容易去理解list_for_each_entry,可以把它看作for()循环
………………
}