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

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值