您的位置:首页 > 运维架构 > Linux

linux内核对于指令异常的处理

2017-01-11 17:21 1236 查看

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

datecontentlinux
2017.1.11linux 4.4
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: