1.处理流程
以arm64来介绍一下流程,如果在用户层发生指令异常时,首先进入入口el0_undef
( arch/arm64/kernel/entry.s )
el0_undef:
/*
* Undefined instruction
*/
// enable interrupts before calling the main handler
enable_dbg_and_irq
ct_user_exit
mov x0, sp
bl do_undefinstr-----------------------------------调用此函数如下
b ret_to_user
( arch/arm64/kernel/traps.c )
asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
{
siginfo_t info;
void __user *pc = (void __user *)instruction_pointer(regs);
/* check for AArch32 breakpoint instructions */
if (!aarch32_break_handler(regs))
return;
if (call_undef_hook(regs) == 0)----------------------进入此函数执行真正的处理过程,正确处理完后,直接返回不进入Oops,相反会出错
return;
if (unhandled_signal(current, SIGILL) && show_unhandled_signals_ratelimited()) {
pr_info("%s[%d]: undefined instruction: pc=%p\n",
current->comm, task_pid_nr(current), pc);
dump_instr(KERN_INFO, regs);
}
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = ILL_ILLOPC;
info.si_addr = pc;
arm64_notify_die("Oops - undefined instruction", regs, &info, 0);
}
( arch/arm64/kernel/traps.c )
static int call_undef_hook(struct pt_regs *regs)
{
struct undef_hook *hook;
unsigned long flags;
u32 instr;
int (*fn)(struct pt_regs *regs, u32 instr) = NULL;
void __user *pc = (void __user *)instruction_pointer(regs);
if (!user_mode(regs))
return 1;
if (compat_thumb_mode(regs)) {
/* 16-bit Thumb instruction */
if (get_user(instr, (u16 __user *)pc))
goto exit;
instr = le16_to_cpu(instr);
if (aarch32_insn_is_wide(instr)) {
u32 instr2;
if (get_user(instr2, (u16 __user *)(pc + 2)))
goto exit;
instr2 = le16_to_cpu(instr2);
instr = (instr << 16) | instr2;
}
} else {
/* 32-bit ARM instruction */
if (get_user(instr, (u32 __user *)pc))
goto exit;
instr = le32_to_cpu(instr);
}
raw_spin_lock_irqsave(&undef_lock, flags);
list_for_each_entry(hook, &undef_hook, node)--------------遍历链表undef_hook中的每一个hook
if ((instr & hook->instr_mask) == hook->instr_val &&
(regs->pstate & hook->pstate_mask) == hook->pstate_val)
fn = hook->fn;----------------------------匹配成功后把hook->fn赋给fn
raw_spin_unlock_irqrestore(&undef_lock, flags);
exit:
return fn ? fn(regs, instr) : 1;--------------------------执行fn,正确执行返回0
}
2.未定义指令的注册
结构体struct undef_hook用来封装一个未定义指令
struct undef_hook {
struct list_head node;
u32 instr_mask;---------------instruction mask匹配用的掩码
u32 instr_val;----------------instruction val匹配的关键值
u64 pstate_mask;
u64 pstate_val;
int (*fn)(struct pt_regs *regs, u32 instr);--相关的处理函数
};
如果想要把一个指令加入到内核的未定义指令处理框架中,要先实现一个结构体insn_emulation_ops
struct insn_emulation_ops {
const char *name;
enum legacy_insn_status status;--有两种:INSN_DEPRECATED和INSN_OBSOLETE
struct undef_hook *hooks;--
int (*set_hw_mode)(bool enable);
};
在这里对于未定义指令可以有两种方式:
(1)INSN_DEPRECATED : 表示指令虽然未定义,但是可以进行处理来使这条指令得以正确执行
(2)INSN_OBSOLETE : 表示指令没有定义,不注册入异常指令处理框架中
2.1 异常指令注册函数
( arch/arm64/kernel/armv8_deprecated.c )
static void register_insn_emulation(struct insn_emulation_ops *ops)
{
unsigned long flags;
struct insn_emulation *insn;
insn = kzalloc(sizeof(*insn), GFP_KERNEL);
insn->ops = ops;
insn->min = INSN_UNDEF;
switch (ops->status) {
case INSN_DEPRECATED:
insn->current_mode = INSN_EMULATE;
/* Disable the HW mode if it was turned on at early boot time */
run_all_cpu_set_hw_mode(insn, false);
insn->max = INSN_HW;
break;
case INSN_OBSOLETE:
insn->current_mode = INSN_UNDEF;
insn->max = INSN_EMULATE;
break;
}
raw_spin_lock_irqsave(&insn_emulation_lock, flags);
list_add(&insn->node, &insn_emulation);----------把定义的异常指令加入到链表insn_emulation中
nr_insn_emulated++;
raw_spin_unlock_irqrestore(&insn_emulation_lock, flags);
/* Register any handlers if required */
update_insn_emulation_mode(insn, INSN_UNDEF);----把待处理的异常指令注册到框架中
}
( arch/arm64/kernel/armv8_deprecated.c )
static int update_insn_emulation_mode(struct insn_emulation *insn,
enum insn_emulation_mode prev)
{
int ret = 0;
switch (prev) {
case INSN_UNDEF: /* Nothing to be done */
break;
case INSN_EMULATE:
remove_emulation_hooks(insn->ops);
break;
case INSN_HW:
if (!run_all_cpu_set_hw_mode(insn, false))
pr_notice("Disabled %s support\n", insn->ops->name);
break;
}
switch (insn->current_mode) {
case INSN_UNDEF:
break;
case INSN_EMULATE:
register_emulation_hooks(insn->ops);----如果指令是可以仿真(也就是可以正常执行的),就调用此函数进行注册
break;
case INSN_HW:
ret = run_all_cpu_set_hw_mode(insn, true);
if (!ret)
pr_notice("Enabled %s support\n", insn->ops->name);
break;
}
return ret;
}
static void register_emulation_hooks(struct insn_emulation_ops *ops)
{
struct undef_hook *hook;
BUG_ON(!ops->hooks);
for (hook = ops->hooks; hook->instr_mask; hook++)
register_undef_hook(hook);
pr_notice("Registered %s emulation handler\n", ops->name);
}
整个注册流程最终就是要调用register_undef_hook来进行注册
( arch/arm64/kernel/traps.c )
void register_undef_hook(struct undef_hook *hook)
{
unsigned long flags;
raw_spin_lock_irqsave(&undef_lock, flags);
list_add(&hook->node, &undef_hook);---------加入到链表undef_hook
raw_spin_unlock_irqrestore(&undef_lock, flags);
}
2.2 内核初始化注册入口
late_initcall(armv8_deprecated_init);
static int __init armv8_deprecated_init(void)
{
if (IS_ENABLED(CONFIG_SWP_EMULATION))
register_insn_emulation(&swp_ops);
if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION))
register_insn_emulation(&cp15_barrier_ops);
if (IS_ENABLED(CONFIG_SETEND_EMULATION)) {
if(system_supports_mixed_endian_el0())
register_insn_emulation(&setend_ops);
else
pr_info("setend instruction emulation is not supported on the system");
}
register_cpu_notifier(&insn_cpu_hotplug_notifier);
register_insn_emulation_sysctl(ctl_abi);
return 0;
}
change log
date | content | linux |
---|---|---|
2017.1.11 | linux 4.4 |